Skip to content
Entwicklung7 min11/10/2026

Expo – what it is, what it takes off your hands, and where it stops

Expo doesn't take the programming off your hands – it takes away everything that used to keep you from starting. What Expo is, what it isn't, and when you're better off without it.

Christopher Groß
Christopher GroßFreelance Developer

I spent about twenty years building for the web. I left native apps alone for a long time – not because they didn't interest me, but because of everything that comes before the first line of code.

Install Xcode. Create a developer account. Generate certificates, assign provisioning profiles, work out what the difference between the two even is. And then somehow get the app onto your own phone.

That isn't programming. That's administration. And it sits right at the start, exactly where motivation is thinnest. What that looks like without any help I later experienced in full on a native iOS app.

This is precisely where Expo comes in. Which is why I find the usual one-liner – "a framework for React Native" – not wrong, but not very helpful either. It doesn't say what it actually helps you with.

Expo doesn't replace React Native

That's the most common beginner mistake, so let's get it out of the way first.

React Native is the foundation. You write TypeScript and React, and instead of HTML in a browser you get real operating-system controls. No embedded browser, no imitated look – a button is an actual button.

Expo is what sits around it: project setup, native configuration, building, shipping, plus a large collection of ready-made modules for things like the camera, fonts or encrypted storage.

So you're not trading one for the other. You use both at once. The official documentation puts it fairly unambiguously: the expo package and its command-line tool work with any React Native app.

An analogy that holds up well enough: React Native is the engine, Expo is the car around it. You can buy an engine on its own. You wouldn't want to drive it.

What actually gets taken off your hands

It starts with one command:

npx create-expo-app@latest my-app
cd my-app
npx expo start

After that you have a running project. No Xcode, no Android Studio, no certificates. On your phone you scan a QR code, and the app is there.

That's the visible part. The more interesting one sits underneath.

The native projects are no longer source code

A native app needs an iOS and an Android project: Info.plist, AndroidManifest.xml, build configuration, permissions. Traditionally those folders live in the repository and are maintained by hand.

Expo inverts that. The principle is called continuous native generation, and it means the ios/ and android/ folders are generated on demand, from a configuration file and a handful of plugins. In freshly created projects they're even listed in .gitignore – they're build output, not source.

What you maintain instead is a description of what should be different:

{
  "expo": {
    "name": "My App",
    "plugins": [
      ["expo-camera", { "cameraPermission": "For scanning barcodes." }]
    ]
  }
}

A config plugin like this writes to the right places while the native projects are being generated – here the permission text into Info.plist and the matching entry into the Android manifest. You don't need to know where those places are.

The price for it sits in the documentation as a warning: if you edit the generated folders by hand, you risk losing those changes on the next run. That's not a flaw, that's the idea. Configuration instead of post-editing.

Two ways to get the app onto a phone

This is where a lot of people stumble, because two similar-sounding things exist side by side.

Expo Go is a finished app from the store. It contains a fixed set of native modules and can run other people's projects inside them. Great for trying things out, but limited: what isn't built in, you can't use.

A development build is the alternative for real projects. Put simply: your own version of Expo Go, built from your own project, with exactly the native libraries you actually need. It feels the same to work with – changes to TypeScript code still show up on the device immediately. You only rebuild when something native changes.

For serious projects this is the normal case. Expo Go is the test drive.

Building and shipping

Two further pieces are optional, but they're the reason many people stay with Expo.

EAS Build builds the app in the cloud. In practice: you can have an iOS app built without a Mac in front of you, and the whole certificate business gets managed rather than explained.

EAS Update pushes changes to the JavaScript part straight onto installed devices, bypassing store review. A typo or a wrongly calculated value is out in minutes instead of days. The boundary matters: changes to native code can't go this way. Those need a new build and the regular route through the store.

Both are Expo's own services with their own pricing, and neither is mandatory – the tools themselves are open source, and you can build and ship from your own infrastructure.

What's already built in

Instead of a feature list, three things I use daily.

Navigation lives in the file system. If you know Next.js, you know the principle: a file in the right folder is a route.

app/
  _layout.tsx
  index.tsx           →  /
  articles/
    index.tsx         →  /articles
    [id].tsx          →  /articles/42

No routing table maintained in parallel that eventually stops matching the folder contents. The square brackets mark a variable part, and inside the file you can get at it:

import { useLocalSearchParams } from 'expo-router'

export default function ArticleScreen() {
  const { id } = useLocalSearchParams<{ id: string }>()
  // ...
}

Fonts, camera, images, location come as individually installable modules that get updated along with everything else. That sounds mundane, but it's the difference between "I add a font" and "I put a file in a native directory and register it in three places".

Credentials go where they belong. A login token doesn't belong in the app's ordinary storage. With expo-secure-store it ends up in the operating system's key store – the Keychain on iOS, the Keystore on Android:

import * as SecureStore from 'expo-secure-store'

await SecureStore.setItemAsync('session', token)
const stored = await SecureStore.getItemAsync('session')

Two lines for something you'd otherwise solve separately per platform.

The objection that no longer applies

Anyone who asked around about Expo two or three years ago reliably ran into one sentence: with Expo you can't get at native code, and if you need it you have to "eject" – once, and irreversibly.

That used to be true. It isn't anymore, and not by a small margin.

The expo eject command was fully removed in SDK 46 in August 2022. The term is gone too: the official FAQ now states in so many words that "eject" is a deprecated term and no longer necessary. The old split into "managed" and "bare workflow" no longer appears as the main distinction in current documentation.

What replaced it is the question of whether you have the native projects generated or maintain them yourself. And between those two there's no longer a one-way street, just a switch.

Concretely, there are four routes to native code today, and none of them is an exit:

  • Install a library with a native part and regenerate the development build.
  • Write a config plugin that adjusts the native files during generation.
  • Write your own native module – the documentation describes this as an intended path, not an emergency exit.
  • Put the ios/ and android/ folders in the repository after all and maintain them yourself. You then give up automatic generation and keep using the rest.

You still have to be honest about it: none of this is free. Every native dependency means a new build. And for libraries that don't ship a config plugin, you write one yourself – manageable, but it's work you didn't have before.

Where Expo stops

The honest test for a tool isn't the list of what it can do. It's the project where it wouldn't have helped. I have one of those.

A small app against doom-scrolling that I built a while back depended, for its main feature, on Apple's Screen Time API. And that one exists only inside native extensions running outside the app itself. No cross-platform tool takes that off your hands, Expo included.

It would have been doable: a custom native module, a config plugin around it. Except then I would have been writing Swift and maintaining the configuration that carries the Swift part into the generated projects. Two layers instead of one – for the very feature the whole app was about.

That turned into my rule of thumb. The question isn't whether you need native code; sooner or later almost everyone does. The question is whether it sits at the core of the app or at its edges. At the edges, Expo is comfortable. At the core, you pay twice.

Three more limits, briefly:

If you have maintained your iOS and Android projects by hand for years, don't switch on automatic generation. The documentation warns about it explicitly, because manual customisations get lost in the process. Individual Expo pieces can still be adopted.

Where the build isn't allowed to leave the building, the most convenient part falls away. Building it yourself is possible, but a good chunk of the comfort lives precisely in the service you then aren't using.

And if there is only one platform and the team works natively anyway, you're paying for an abstraction nobody benefits from.

That leaves one point that weighs more in daily work than it sounds: you tie yourself to someone else's release rhythm. You update when Expo updates, not when it suits you. To be fair, that has eased off lately – the current SDK ships React Native 0.86, React stays unchanged, and the jump was explicitly planned without breaking changes. Upgrading is essentially one command. But the tempo is set elsewhere.

What Expo doesn't change

So there's no wrong impression: app review doesn't get any friendlier because of it. You still have to learn the habits of both platforms – how navigation should feel, when a screen deserves a route of its own, why the same layout looks wrong on Android. And an app that feels good is still work.

Expo only shifts when you learn that. No longer up front, as an entry fee, but at the point where you actually need it.

That was the difference for me. The administration used to come first and the building afterwards – if you wanted to try an idea one evening, you spent the evening on certificates. Today the idea comes first, and all the rest arrives only when it's genuinely meant to reach the store.

For my nutrition app that was the reason to stay with Expo – and it's still my default whenever I build apps for iOS and Android. Not because it can do everything. Because none of it stopped me on the first evening.

Expo doesn't make building an app easy. It makes starting easy – and that is where most ideas die, long before things get hard.

Christopher Groß
$ whoami

Christopher Groß

Fullstack Developer & AI Orchestrator from Hamburg

Christopher Groß has been building web applications for startups and agencies for over 20 years. His focus is on Vue.js, Nuxt, and AI-powered development. He believes in clean code, clear specs, and coffee in large quantities.

Want more?

Want to know how I work and what drives me? Reach out – 30 minutes, a real conversation about tech, AI and projects.

Book a call

Write to me directly: moin@grossbyte.io

Response within 24h
Relaxed conversation
No commitment