●MAX — Rork Max is built on Claude Code and Claude Opus 4.6, generating native Swift apps directly instead of React Native●APPLE — Rork Max targets the whole Apple ecosystem: iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●WORKFLOW — In practice, users settle into letting the AI scaffold while they rewrite the state management and data layer themselves●SEED — Rork raised a $15M seed led by Left Lane Capital in April, with Peak XV, True Ventures, and a16z Speedrun joining●PAPERLINE — Rork acquired app builder Paperline and says it will stay acquisitive to bring in engineering talent●REVIEW — Three-month revisit reviews are growing, clarifying where the tool shines and where it doesn't●MAX — Rork Max is built on Claude Code and Claude Opus 4.6, generating native Swift apps directly instead of React Native●APPLE — Rork Max targets the whole Apple ecosystem: iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and iMessage●WORKFLOW — In practice, users settle into letting the AI scaffold while they rewrite the state management and data layer themselves●SEED — Rork raised a $15M seed led by Left Lane Capital in April, with Peak XV, True Ventures, and a16z Speedrun joining●PAPERLINE — Rork acquired app builder Paperline and says it will stay acquisitive to bring in engineering talent●REVIEW — Three-month revisit reviews are growing, clarifying where the tool shines and where it doesn't
Designing Empty States Properly in Your Rork App — First Run, After Deletion, and Network Errors in One Component
When you build an app from a prompt in Rork, only the data-filled screens tend to look polished. Here is how to build the first-run, post-deletion, and network-error empty states into one reusable component, with retry logic, screen-reader support, and effectiveness measurement.
Have you ever installed your freshly built Rork app, opened it for the first time, and felt a little lost staring at a blank list with not a single row in it? Apps generated from a description prompt come out remarkably clean when there is data to show. The screens for when there is nothing yet, though, barely get any attention unless you ask for them explicitly.
These "nothing here" screens are called empty states. They are easy to overlook, yet every user passes through them the moment they open your app for the first time. Leave them blank and people quietly assume something is broken, then drift away. As an indie developer, I have shipped a number of apps to the App Store and Google Play, and the single change that reduced first-day churn the most was, surprisingly, taking the time to polish these empty screens.
This article goes past simply "dropping in some text." We will support the first-run, post-deletion, and network-error states from one reusable component, then build in retry logic, screen-reader support, and effectiveness measurement. Rather than leaving everything to Rork, we will look at exactly where to edit the generated code to change the quality.
An empty screen is not just one thing
If you treat empty states as "the screen when there is no data," you will design them wrong. The same blankness covers three very different situations, and each one calls for a different message.
The first is the first-run empty state: the app has just been installed and the user has registered nothing yet. What they want to know here is simply, "What do I do next?"
The second is the post-deletion empty state: every task is done, or every saved item has been cleared. This feels nothing like the first run. The user already knows how things work, so instructions are unnecessary. A small sense of accomplishment, or a gentle nudge toward the next step, fits much better.
The third is the network-error empty state: there is data, but a failed fetch makes the screen look empty. If you show "Nothing here yet" in this case, the user will think their data is gone. The right message is something like, "We couldn't load this. Please try again," paired with a way to retry.
Just separating these three changes the feel of your empty screens completely. And in code, it pays off to handle these three plus a "loading" case in one place, rather than scattering the same branch across every screen.
How to prompt Rork for empty states
Rork generally won't build what you don't describe. The flip side is that if you ask for empty states directly, it will produce them. Adding a line like this when generating works well:
For the list screen, create a first-run empty state for when there are zero items.Center a soft illustration or icon, a "No ◯◯ yet" heading, a one-line note,and an "Add your first ◯◯" button.Use different headings and copy for the post-deletion and network-error cases,and implement all of them as a single reusable component.
The key is to insist that the empty state include a button driving the next action, and to make it explicit that you want the three states bundled into one component. An empty state is not just a notice; it is the entry point that guides the user to their first interaction. Even if your app is already generated, you can tell Rork in chat, "The screen when there are zero tasks feels bare — please add first-run, post-deletion, and error variants bundled into one component," and it will add just that screen.
✦
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
✦A reusable single-component design that switches between first-run, post-deletion, and error states, with a prompt you can hand straight to Rork
✦Exponential-backoff retry for network errors, plus a loading skeleton that stops the empty state from flickering
✦How to make empty states screen-reader friendly, and how to measure which empty state users drop off at so you can improve the copy
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.
Peek at the code Rork generates and you can see how the empty states are controlled. Often each screen has its own copied if (isLoading) ... if (items.length === 0) ... branch, so every fix means editing every screen. Collapse this into a single <ListStateView> and you can manage both the copy and the design in one place.
type ListState = "loading" | "error" | "empty-first" | "empty-cleared" | "ready";function resolveListState( isLoading: boolean, hasError: boolean, itemCount: number, hasEverHadItems: boolean,): ListState { if (isLoading) return "loading"; if (hasError) return "error"; // ← always before the empty check if (itemCount === 0) { return hasEverHadItems ? "empty-cleared" : "empty-first"; } return "ready";}function ListStateView({ state, onRetry, onAdd, children }) { switch (state) { case "loading": return <ListSkeleton rows={6} />; case "error": return ( <EmptyState icon="cloud-off" title="Couldn't load" message="Please check your connection and try again" actionLabel="Reload" onAction={onRetry} /> ); case "empty-first": return ( <EmptyState icon="sparkles" title="No items yet" message="Tap the button at the bottom right to add your first item" actionLabel="Add item" onAction={onAdd} /> ); case "empty-cleared": return ( <EmptyState icon="check-circle" title="All done" message="That's everything for today. Nice work" actionLabel="Add new" onAction={onAdd} /> ); default: return children; }}
What matters in this branch is putting the hasError check beforeitemCount === 0. Reverse the order and a failed fetch with an empty array will also show the first-run message, making users suspect their data has vanished. One more thing: keeping a hasEverHadItems flag (whether items have ever existed) lets you mechanically tell the first-run and post-deletion cases apart. A locally stored flag or a cumulative-count counter is plenty. It is worth confirming once that the generated code follows this branch.
Use a loading skeleton to stop the empty state from flickering
A surprisingly common oversight is the empty state flashing for a split second while data loads. During the few hundred milliseconds the fetch is running, items is still an empty array, so the first-run empty state flickers into view and the list pops in right after. That flicker is a quiet reason apps feel unstable.
The fix is simple: show a skeleton (a faint placeholder shaped like the list) while loading. Showing the outline of the list that is about to appear makes the wait feel shorter than spinning a spinner.
When asking Rork, say "While loading, show a skeleton shaped like the list rows rather than a spinner," and it will generate something close to this. The reason resolveListState gives loading top priority is precisely so that the empty-state branch is never reached while isLoading is true.
Don't let network errors end at "just tap again"
Putting a "Reload" button on the error empty state is a first step, but if the user taps repeatedly and it keeps failing silently, they will close the app anyway. A slightly smarter retry steadies the experience. Concretely: add exponential backoff that lengthens the wait after each failure, and only show the error screen once several attempts have failed.
On top of that, caching the most recently fetched data on the device lets you show "what they saw last time" even offline, sparing them the empty state entirely. Tell Rork, "Cache data in AsyncStorage, and if a fetch fails, show the cache first and then quietly refetch," and it will build along those lines. Treating the full empty state as the last resort — for when there is no cache and genuinely nothing to show — gives the calmest experience.
Make empty states screen-reader friendly
It is easy to focus on the visual design of empty states, but for people using a screen reader (VoiceOver on iOS, TalkBack on Android), a state that isn't read aloud leaves them with no idea what is on screen. You want that one carefully chosen line to reach them by voice, too.
In React Native, give the empty-state container an accessibilityRole and accessibilityLabel so the heading, description, and button are announced in order.
When the screen state changes — say, after recovering from an error — and you want to actively signal it, adding a short announcement like AccessibilityInfo.announceForAccessibility("Items loaded") is a kind touch. The surest way to check this is to assign VoiceOver to the accessibility shortcut on iOS and actually trace the screen with your finger on a real device.
Watch all three states with your own eyes
No matter how carefully you design them, you can't know how they look until you see them on a device. Reproduce each of the three on purpose. The first-run state appears if you reinstall the app or clear all data. The post-deletion state shows up as you remove your registered items one by one.
The slightly fiddly one is the network-error state. You can reproduce it easily by turning on airplane mode and then opening a screen that fetches data. Tap "Reload" while still in airplane mode, check that the error screen doesn't freeze, then turn airplane mode off and tap again to confirm the data comes back. Running that round trip once lets you verify ahead of time how the app behaves when a user loses signal. With apps that carry AdMob ads, I've found that layout breakage during unstable connections feeds straight into review ratings, so this is a check I never skip.
One more thing to watch: whether the transition from skeleton to real data, or skeleton to error, is smooth. If the layout jumps hard at the moment of the switch, your skeleton ends up doing more harm than good.
Measure which empty state users drop off at, then improve the copy
Once you have built things this far, the last step is to make "which empty state is working" visible as numbers. An empty state only matters when it is not just shown but leads to a tap on the button and the next action. Recording the display and the tap as separate events gives you something to act on.
useEffect(() => { if (state.startsWith("empty")) { analytics.track("empty_state_shown", { variant: state }); }}, [state]);// When the button is tappedfunction handleAction() { analytics.track("empty_state_cta_tapped", { variant: state }); onAdd();}
In one of my own apps, simply changing the first-run heading from "No data" to "Try adding your first entry" noticeably lifted the tap rate on the add button. Comparing the ratio of taps to impressions over a week or two makes it clear which copy resonates. The trick is to change only one piece of copy at a time; change several at once and you can't tell which one moved the needle. It is humble measurement, but precisely because empty states are where drop-off tends to happen, they are worth polishing with numbers.
Tone and whitespace convey care
Because an empty state shows so little, every word stands out. A screen that says only "No data" and one that says "No records yet. Why not start with today?" leave completely different impressions. The first feels mechanical; the second keeps a trace of the writer's warmth. It helps to match the wording of your empty states to the overall voice of the app.
Place an illustration or icon in the center with generous space above and below, and the blankness starts to read as intentional calm rather than something unfinished. This part is hard to invest much in, but with Rork you can simply ask, "Add a friendly illustration to the empty state," and the mood comes together. For the broader thinking on screen design, the UX Design Patterns for Rork Apps piece covers related ground.
Start with the first-run empty state
Trying to perfect all three at once tends to stall you. The one to fix first is the first-run empty state, since it is the screen the most users are guaranteed to pass through. A single button pointing to the next step there visibly changes first-day retention. Open your app right now and clear its data to see that screen. If it looks bare, start by collapsing the first-run empty state into ListStateView, and once you're comfortable, widen it one step at a time to retry, screen-reader support, and measurement. If you'd like the bigger picture of Rork itself, see Rork — Overview of the Mobile App Platform Built with AI.
Thank you for reading. The smaller the screen, the more clearly it shows the care you put into your users.
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.