◉RORK LABJP
●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●RORK — GPT-6.1 Sol (Sep 29) and Claude Sonnet 5.5 (Sep 28) are now in the model menu●RN 0.88 — The stable release was still unpublished on its Oct 12 target. rc.4 (Oct 7) is the newest; 0.87.1 is Latest●Q&A — Readers report expo-widgets showing a black widget in production builds only●WINDOWS — A walkthrough shows how to ship an iPhone app from Windows only, with no Mac●NEW — Three checks to run first when notifications stop arriving in Expo Go●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●RORK — GPT-6.1 Sol (Sep 29) and Claude Sonnet 5.5 (Sep 28) are now in the model menu●RN 0.88 — The stable release was still unpublished on its Oct 12 target. rc.4 (Oct 7) is the newest; 0.87.1 is Latest●Q&A — Readers report expo-widgets showing a black widget in production builds only●WINDOWS — A walkthrough shows how to ship an iPhone app from Windows only, with no Mac●NEW — Three checks to run first when notifications stop arriving in Expo Go
Articles/Business
⟐ Business/2026-04-28Advanced

How to Make Ads and Subscriptions Coexist in a Rork App: A Solo Developer's Hybrid Revenue Model

I used to think ads and subscriptions don't mix. After a year of experiments on my own apps, I learned that with the right boundary design, a hybrid produces nearly 1.7x the revenue of either model alone. Here's the model I'm shipping, with implementation code and real numbers.

Rork578app monetization6AdMob71subscription28indie development42

✦ Premium Article

For a long time, I was skeptical of putting both ads and subscriptions into the same app. The reasoning seemed obvious: "if you've already annoyed users with ads, layering a subscription on top just speeds up the exit."

After a year of experiments on my own apps, I changed my mind. With the right boundary design, ads and subscriptions don't fight — and the combined monthly revenue ran roughly 1.7x what either model produced alone. Most of the apps I'm building with Rork right now use a hybrid configuration.

This piece walks through the hybrid revenue model I actually run as a solo developer, with the design intent behind each decision and the implementation I hand-add on top of the React Native code Rork generates. Ad gating and subscription-state synchronization sit just outside what Rork writes for you automatically — and getting that seam right is, in my experience, what separates a hybrid that earns from one that annoys.

"Ads or subscription" is the wrong frame

The "ads or subscription" debate only makes sense if you treat all your users as one group. Real app users vary wildly across willingness to pay, frequency of use, and tolerance for ads.

Heavy ad tolerance, "I will never pay" users. Hate ads but won't pay a few hundred yen a month. Will pay monthly but balk at annual. Will buy annual outright. Will pay once for a lifetime unlock and nothing after.

I can clearly see at least these five segments in my own apps. Picking one plan means abandoning four of them. The hybrid model is a direct response to that segmentation.

Here are the real numbers from one of my wallpaper apps. It's a single example from a single app, but it's a useful starting point for reasoning about whether the design holds up.

User segmentShareMonthly revenue per user (rough)Contribution to total
Non-paying (ads only)~80%10–25 JPY~35% of total
Subscribers~8%280–480 JPY~50% of total
One-time buyers~4%~120 JPY equivalent, first month only~8% of total
Active on ads only~8%15–30 JPY~7% of total

What I most want this table to say is that half the revenue comes from the 8% who subscribe, and the other half from the thin, steady accumulation across the non-paying base. Optimize for only one and your design throws the other away. The hybrid exists to catch both at once.

Don't draw the boundary along features

The classic mistake is to split by feature: "Feature A is free with ads, Feature B is subscription only." From the user's view, this reads as "you keep adding things I can't use unless I pay."

I draw the boundary on two axes: frequency and quality.

Frequency-wise, an example: "save up to three wallpapers per day for free, beyond the fourth either watch a rewarded ad or upgrade." The feature itself is open to everyone — only the cap is paywalled, with two ways to lift it. The ad-tolerant and the subscription-friendly each have a home.

Quality-wise: "free is standard resolution, subscription is 4K." This isn't taking a feature away — it's offering an upgrade for those who want higher quality.

When you cut along these two axes, users rarely feel funneled. In my data, conversion to subscription is noticeably higher than the feature-split approach.

Implementing the daily frequency cap

A frequency boundary that exists only as a concept falls apart in the code. Here's the minimal daily counter I run in React Native (the shape Rork generates). It resets when the local date changes, and once the free cap is exceeded it reports whether the unlock path is "ad" or "subscription."

// useDailyQuota.ts — minimal management of daily free usage
import AsyncStorage from "@react-native-async-storage/async-storage";
 
const FREE_DAILY_LIMIT = 3;
 
function todayKey() {
  // Slice the day on the device-local date (slicing on UTC drifts at night)
  const d = new Date();
  return `quota:${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`;
}
 
export async function getUsedCount(): Promise<number> {
  const raw = await AsyncStorage.getItem(todayKey());
  return raw ? parseInt(raw, 10) : 0;
}
 
export async function consumeOne(): Promise<void> {
  const key = todayKey();
  const used = await getUsedCount();
  await AsyncStorage.setItem(key, String(used + 1));
}
 
// Report whether the cap is exceeded, and if so whether "ad" or "subscription" unlocks it
export async function checkQuota(isSubscriber: boolean) {
  if (isSubscriber) return { blocked: false, unlockBy: "subscription" as const };
  const used = await getUsedCount();
  if (used < FREE_DAILY_LIMIT) return { blocked: false, unlockBy: "free" as const };
  return { blocked: true, unlockBy: "ad" as const };
}

What I deliberately avoid here is holding the count server-side. In the early stage of solo development, device-local works fine. Routing through a server means a network call on every launch, and a user who hits the wall offline gets confused. Start with AsyncStorage, and add server validation only once abuse becomes a problem you can't ignore — that's the order I recommend.

Why slice the date on the device? Because slicing on UTC resets the count at 9 p.m. for users in Japan. I actually shipped this once — "the cap comes back early, but only at night" — and only noticed when a review pointed it out. Small thing, but it goes straight to the trustworthiness of your frequency design.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Drawing the ads-vs-subscription boundary along frequency and quality so users never feel funneled, with the implementation behind it
✦Startup state-synchronization code that prevents the classic accident of loading ads before the subscription check resolves and showing ads to paying users
✦The two-layer structure that earns revenue from the 80 percent who never pay, plus day-by-day offer logic that targets the first-30-day conversion window
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

⟐ Business2026-05-03
From Rork to $1,000/month with AdMob — A Practical Indie Developer's Monetization Playbook
How to take a Rork-built mobile app toward meaningful monthly AdMob revenue: category fit, getting the ATT-before-initialize order right, time-based interstitial pacing, judging by ARPDAU, app-ads.txt, and running a small portfolio.
⟐ Business2026-05-02
A Solo Developer's Strategy for Pushing a Mobile App Past ¥100k/Month — Real Revenue Tuning Tested on Rork-Built Apps
The hands-on strategy for breaking the ¥100k/month wall in solo mobile development. Ad eCPM optimization, IAP tuning, subscription retention, and ASO — every lever I've actually tested on Rork-built apps over 12 years of solo monetization, with code and numbers.
⟐ Business2026-05-02
Mobile App Monetization Foundations With Rork — Choosing Between Ads, IAP, and Subscriptions
A monetization primer for Rork-built mobile apps: ads, in-app purchases, and subscriptions compared by fit and implementation pitfalls, plus a small estimator that turns your own DAU, conversion, and churn assumptions into side-by-side monthly revenue, AdMob mediation practice, and cohort retention code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links