Skip to content

When not to use server-driven UI (we sell it)

Build, buy & decideOpinion5 min readMiniwall team

Keep a screen in the app when it rarely changes, when its hard part is behavior and not layout, when it sits on a hot path, when it has to act differently in each app version, or when nobody will own the schema. We build a tool that renders screens from the server, so this is a slightly awkward post to write. It's also the one we'd want to read before buying anything like it.

Each case below ends with a test. If the test says yes, leave the screen where it is. If you need the basics first, the field guide covers what server-driven UI is, and where it helps and where it hurts is the general version of this argument.

It almost never changes

A remote screen costs you a renderer, a format, validation and a way to roll back. You pay that every week, whether or not anyone edits anything. A settings page or a legal screen that moves twice a year is cheaper to change with a normal release.

Keep it in the app if you can't name a person who asked for a change to this screen in the last few months. If the answer is "engineers touched it, during a release that was happening anyway", there's nothing to remove.

The hard part is behavior

A server can send text, images, order and style. It struggles to send logic. Flutter's package for remote widgets says so about itself: its widget libraries are declarative, and "they cannot represent executable closures"[2]. That is a design choice, and a sensible one, but it shows where the edge sits. Every format ends up here. Once your screen needs conditions, loops and callbacks inside the description, you are slowly writing a programming language, and a worse one than the one in your app.

Keep it in the app if the last five requests for this screen were about what a tap does and not about what the screen says. Drag and drop, custom gestures, and animation tied to touch belong here too.

It's on a hot path

A remote screen adds a request, a cache decision and a check before the first pixel. You can make that fast. But the cheapest request is the one you don't make. A launch screen, a camera view, or anything that has to appear before the network answers is a poor candidate.

I haven't benchmarked this and won't pretend to. It's a design argument: fewer moving parts on the path to the first frame means fewer things to go wrong there.

Keep it in the app if you'd be embarrassed by a blank frame or a spinner on that screen, even once a month.

It must act differently in each app version

Some screens are tied to the build. They call a native API that older builds don't have, or they sit in front of a permission prompt, or they only make sense next to code that shipped last Tuesday. A description sent to every version will meet old builds that can't do what it asks.

There's a store angle too, and we'll stay careful with it. Apple's guideline 2.5.2 is about apps that "download, install, or execute code which introduces or changes features or functionality of the app"[1], as of 2026-10-27. Data that only arranges components the app already contains is a different thing from code. But the closer a remote description gets to changing what the app does, the more you should read that section yourself, and not take our word or anyone's summary. We're not telling you where the line is.

Keep it in the app if a change to this screen would also need a native change to work. Ship both together as a release. Change a live app without a release lists lighter options for the cases where only a value changes.

Nobody will own it

This is the one we'd rank first. Someone has to say no to the fortieth component type, review what non-engineers publish, and decide what an old app does with a block it has never heard of. If that someone is "whoever has time", the schema grows by accident.

Ownership also means deciding who gets to release. Splitting deploy from release is a good idea, and deploy is not release argues for it, but it only works when a person is accountable for the second half.

Keep it in the app if you can't answer three questions with names: who approves a new component, who reviews a published edit, and who rolls it back at 6 pm on a Friday.

CaseKeep it in the app if
Rarely changesYou can't name who asked for the last change
Behavior-heavyRequests are about what a tap does, not what it says
Hot pathA blank frame or spinner would be a bug
Version-exactA change would also need native code
No ownerYou can't name who approves, reviews and rolls back

Swipe sideways to see every column.

When this advice doesn't apply

Flip every test and you get the case for going remote. The screen changes often, people who don't ship builds want to change it, the layout is mostly text, images and buttons, and a named person owns it. Your release cycle also matters: if you ship daily on one platform, the wait you'd remove is small, and if each release takes a week, the wait is large. That is why we think of promotions, upgrade prompts and onboarding as the usual good fits, and the core of the product as the usual bad one.

Sources

  1. Apple, App Review Guidelines (section 2.5.2) (opens in a new tab)developer.apple.com · accessed

Go further