frontend
Expo-managed React Native, solo
Expo-managed React Native, solo
Bare React Native means managing native iOS and Android project files directly — real power, real complexity, a heavy maintenance burden for one person. Expo's managed workflow abstracts that: write React Native and TypeScript, Expo handles the native build pipeline, provides pre-built wrappers for camera, push, filesystem and secure storage without native code, and gives OTA updates for JS-only changes with no store resubmission.
Push notifications go through expo-server-sdk server-side. The app registers on launch, gets a device push token, sends it to the backend, which stores it in device_push_tokens keyed to the user with a platform field and an is_active flag. When something needs to go out, the outbox processor picks up the event and calls the SDK with the stored tokens.
const messages = tokens.map((token) => ({
to: token,
sound: "default",
title: notification.title,
body: notification.body,
data: notification.payload_json,
}));
await expo.sendPushNotificationsAsync(messages);
Tokens live in their own table rather than a field on users because a user can have multiple active devices — phone and tablet, or a reinstall generating a new token while the old one's still registered somewhere. is_active lets the app send to every valid device and mark stale ones inactive when Expo reports a token as dead.
A few mobile realities that don't exist on web
- App store review cycles mean a native binary change — a new module, a permission change — takes days of lead time, unlike a web deploy live in minutes. That's exactly why OTA updates matter: JS-only fixes ship instantly, anything touching native code can't.
- Background and foreground state — a mobile app gets killed or loses connectivity far more unpredictably than a browser tab, so realtime features need explicit reconnect-on-foreground handling a web app mostly gets for free.
- Dark mode and RTL have to be first-class, not inherited — mobile can't reuse web's CSS the way a responsive layout can, so every visual adjustment has to be deliberately mirrored in the React Native styling layer.
Mobile release is a genuinely different pipeline from the web apps' Docker build-push-deploy flow — Expo's build service and store submission, nothing in common with a Docker image push — which is why it's a completely separate workflow.