Expo React Native Web Preview Deployment: Live URLs on Every PR Without Touching CI
Your team ships a React Native app. You target iOS, Android, and — since Expo SDK 52 — the web. Your designer reviews mobile screens in Expo Go, but the web version? That lives in a staging environment that only one person can push to, and nobody remembers the URL.
So the web target rots. A button looks wrong on desktop. The responsive layout breaks at tablet width. A critical bug in the web build ships to production because nobody reviewed it — literally nobody. The web target was never part of the PR review flow because setting up a web preview for every branch was "too much CI overhead."
This is the web preview gap, and it's the most common quality failure in Expo + React Native teams.
Here's how to close it — without touching your CI config.
Why the web target matters (even if you're mobile-first)
Expo's web support improved dramatically between SDK 50 and SDK 54. As of 2026:
expo-routerv4 supports file-based routing for web, iOS, and Android from a singleapp/directoryreact-native-web0.20 renders with near-parity to React DOM for most components- Expo's Metro bundler produces production-optimized web bundles via
expo export:web - PWA support means your web build is installable on desktop and works offline
In practice, this means your React Native app already has a completely functional web version. It's not a separate codebase. It's not a "maybe someday" project. It's built into every npx expo export command.
The question isn't whether the web target exists. It's whether anyone ever sees it before it ships.
The current state: what most teams do
Here's the typical Expo + web workflow:
Developer pushes feature branch
→ CI runs `npx expo export:web` (sometimes)
→ CI runs unit tests (usually)
→ PR is reviewed based on code diffs and mobile screenshots
→ Web build is never reviewed
→ Bug ships to production
→ User reports: "Your site looks broken on my iPad"
→ Team: "We have a site?"
The gap is review. Not build. Not deploy. Review. And the reason review doesn't happen is that there's no URL — nothing the designer can click, nothing the PM can tap through on their phone, nothing QA can run through their test plan.
The fix: automatic preview URLs for every branch
The goal: every branch that targets web gets a live URL. No CI config. No Dockerfile. No DNS records.
Here's how it works with PreviewDrop:
- Connect your Expo repo (via GitHub integration)
- PreviewDrop detects
package.json→expo→ web target → runsnpx expo export:web - Serves the output on a unique HTTPS URL
- Comments the URL on the PR
- Tears it down when the PR merges
Setup time: 90 seconds. No config files. No YAML. No CI changes.
What PreviewDrop detects in your Expo project
When you connect an Expo repo, the platform looks for these signals:
// package.json — key fields PreviewDrop reads
{
"dependencies": {
"expo": "~54.0.0", // Detects Expo SDK version
"react-native-web": "~0.20.0", // Confirms web target
"expo-router": "~4.0.0" // Optional: file-based routing
},
"scripts": {
"build:web": "expo export:web" // Build command for web
}
}
If react-native-web is a dependency, PreviewDrop knows this project targets the web. It runs the web build automatically alongside the mobile CI pipeline — not replacing it, just adding the web preview layer.
Step-by-step: setting up Expo web previews
1. Verify your web build works locally
Before connecting anything, make sure expo export:web works on your machine:
# Install dependencies
npm install
# Export the web build
npx expo export:web
# The output goes to dist/ by default
# Test it with a local server
npx serve dist
Open http://localhost:3000 in your browser. If it renders your app, you're ready. If not, fix the web build before proceeding.
Common issues:
Text strings must be rendered within a <Text> component: You have raw text outside a<Text>wrapper. React Native tolerates this;react-native-webdoes not.window is not defined: You're accessing browser APIs during SSR. Guard withtypeof window !== 'undefined'or useuseEffect.- Missing
react-native-web: Install it:npx expo install react-native-web react-dom.
2. Set up your preview environment variables
Expo web builds often need environment variables. Create a .env.preview file:
# .env.preview
EXPO_PUBLIC_API_URL=${PREVIEW_URL}/api
EXPO_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
EXPO_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
The ${PREVIEW_URL} token is replaced at deploy time with the actual preview URL. This is critical for Expo apps where EXPO_PUBLIC_* variables are inlined at build time — you can't change them after the bundle is created.
In your app code, access these variables the standard Expo way:
// app.config.ts or constants.ts
const apiUrl = process.env.EXPO_PUBLIC_API_URL || 'http://localhost:3000/api';
export { apiUrl };
3. Connect your repo
In PreviewDrop, click "New Project" → authenticate with GitHub → select your Expo repo. That's it.
The first build takes 2-4 minutes (installing dependencies, running expo export:web, serving the output). Subsequent builds are faster because dependency layers are cached.
4. Open a PR and verify
Push a branch, open a PR. Within ~3 minutes, you'll get a GitHub comment:
🚀 Web preview: https://pr-42.previewdrop.dev
iOS/Android: Use Expo Go with exp://your-app/@preview/pr-42
Share the URL with your designer, PM, and QA. They can now review the web build without pulling the branch locally.
How this changes your review workflow
Before:
- Designer reviews mobile screens in Expo Go
- Web build is not reviewed
- Bugs caught in production
After:
- Designer reviews mobile screens in Expo Go and the web version via the preview URL
- PM clicks through the full user flow on desktop before approving
- QA runs their web test plan against the preview URL
- Accessibility audits run against the preview URL (Lighthouse, axe-core)
- Cross-browser testing happens before merge, not after
The key shift: web review becomes part of the PR checklist, not a post-deploy fire drill.
The Expo + web stack in 2026: what's supported
Here's what works in Expo web previews today:
| Feature | Status | Notes |
|---------|--------|-------|
| expo-router file-based routing | ✅ Full support | app/ directory works identically across platforms |
| react-native-reanimated animations | ✅ Web support | Layout animations work via react-native-web 0.20 |
| expo-camera / expo-barcode-scanner | ⚠️ Degraded | Web APIs for media access; functional but limited |
| expo-notifications | ⚠️ Partial | Push notifications work via service workers; in-app works |
| expo-secure-store | ⚠️ Fallback | Falls back to localStorage on web; not secure for tokens |
| expo-font | ✅ Full support | Custom fonts load via CSS @font-face |
| react-native-svg | ✅ Full support | Renders as inline SVG |
| PWA / offline mode | ✅ Supported | expo export:web produces a service worker |
| Dark mode / useColorScheme() | ✅ Full support | Respects prefers-color-scheme media query |
For features marked ⚠️, always test the web preview before merging. The mobile version may behave differently from the web polyfill.
The alternative: DIY Expo web previews with GitHub Actions
You can build this yourself. Here's what the GitHub Actions workflow looks like:
# .github/workflows/expo-web-preview.yml
name: Expo Web Preview
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx expo export:web
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
This works, but it requires:
- A Vercel account (or Netlify, or Cloudflare Pages, or any static host)
- Managing API tokens in GitHub Secrets
- Debugging why Vercel's
vcCLI sometimes fails on Expo output paths - Remembering to update the workflow when the Expo SDK version changes and
export:weboutput format changes (it changed between SDK 51 and 52)
For a deeper comparison of DIY vs managed approaches, check out our GitHub Actions preview environment tutorial — it walks through the full DIY workflow and the tradeoffs.
Real-world impact: a case study
A React Native team at a fintech startup (40 engineers, 2 platforms — iOS/Android + web admin panel) implemented this flow. Before:
- 3-4 web bugs shipped per release cycle
- Design review for web happened in a shared staging environment
- "Works on my machine" was the standard defense for web issues
After (3 months with branch web previews):
- Web bugs caught in PR review dropped to near zero
- Designers reviewed web changes alongside mobile in the PR flow
- PM ran user acceptance tests on the preview URL before approving merges
- Average PR review time decreased by 25% because reviewers didn't need to pull branches locally
The setup didn't add a single line of CI config. It didn't require a Dockerfile. It didn't change the team's development workflow. It just gave every branch a URL.
One caveat: monorepos with separate web and mobile packages
If your Expo project lives in a monorepo where the web build is in a separate package (e.g., apps/mobile for React Native and apps/web for Next.js), the auto-detection works differently. PreviewDrop detects the framework per-package and can build both independently, giving each branch two preview URLs — one for mobile (if it has a web target) and one for the web app.
If your monorepo uses Turborepo or Nx, the build commands can be chained:
{
"scripts": {
"build:preview": "turbo run build:web --filter=@myapp/mobile"
}
}
Set this as the custom build command in PreviewDrop's project settings, and every branch gets a URL for the mobile web target.
Your Expo app already has a web version. Give every branch a URL, and your designer, PM, and QA will review it before it ships — the same way they review mobile screens.
Start setting up branch web previews → No credit card, no CI config, works with Expo SDK 50+.
Ready to give every branch a live URL?
Free tier — 2 concurrent previews, no credit card required.
Start free