Skip to main content

Overview

Quest Hunter can be deployed as a static web application using Expo’s web support. This guide covers building and deploying the web version to various hosting platforms.

Web Configuration

Quest Hunter is configured for web deployment in app.json:
"web": {
  "output": "static",
  "favicon": "./assets/images/favicon.png",
  "bundler": "metro"
}

Key Features

  • Static Output: Generates static HTML, CSS, and JavaScript files
  • Metro Bundler: Uses Metro for consistent bundling across platforms
  • Custom Favicon: Configured favicon for web browsers
  • React Native Web: Translates React Native components to web
Quest Hunter uses Metro bundler for web builds, ensuring consistency with native builds and supporting the new React Native architecture.

Building for Web

Development Build

Run the development server:
1

Start development server

bun web
# or
expo start --web
This starts the web development server at http://localhost:8081.
2

Development with backend

Run both web and backend services:
bun dev
This uses mprocs to run:
  • Expo development server
  • Convex backend development server

Production Build

Create a production-optimized static build:
1

Set environment variables

Ensure your .env file has production values:
EXPO_PUBLIC_CONVEX_URL=https://your-production.convex.cloud
EXPO_PUBLIC_CONVEX_SITE_URL=https://your-production.convex.site
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
2

Build static files

bun build
# or
expo export
This generates static files in the dist/ directory.
3

Verify build output

Check the generated files:
ls -la dist/
You should see:
  • index.html - Main HTML file
  • _expo/static/ - Bundled JavaScript and assets
  • assets/ - Images and other assets

Deployment Platforms

Vercel

Deploy to Vercel for optimal performance:
1

Install Vercel CLI

npm install -g vercel
2

Configure vercel.json

Create vercel.json in your project root:
{
  "buildCommand": "bun build",
  "outputDirectory": "dist",
  "devCommand": "bun web",
  "installCommand": "bun install",
  "framework": null,
  "env": {
    "EXPO_PUBLIC_CONVEX_URL": "@convex-url",
    "EXPO_PUBLIC_CONVEX_SITE_URL": "@convex-site-url",
    "EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY": "@clerk-publishable-key"
  }
}
3

Add environment variables

vercel env add EXPO_PUBLIC_CONVEX_URL
vercel env add EXPO_PUBLIC_CONVEX_SITE_URL
vercel env add EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY
4

Deploy

# Deploy to preview
vercel

# Deploy to production
vercel --prod

Netlify

Deploy to Netlify:
1

Install Netlify CLI

npm install -g netlify-cli
2

Configure netlify.toml

Create netlify.toml:
[build]
  command = "bun build"
  publish = "dist"

[build.environment]
  NODE_VERSION = "24.14.0"

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
3

Deploy

# Initialize and deploy
netlify init
netlify deploy --prod

GitHub Pages

Deploy to GitHub Pages:
1

Build for GitHub Pages

bun build
2

Configure homepage

Add to package.json:
{
  "homepage": "https://yourusername.github.io/quest-hunter"
}
3

Deploy with gh-pages

npm install -g gh-pages
gh-pages -d dist
GitHub Pages doesn’t support environment variables at build time. You’ll need to build locally with production environment variables or use GitHub Actions.

AWS S3 + CloudFront

Deploy to AWS:
1

Build static files

bun build
2

Create S3 bucket

aws s3 mb s3://quest-hunter-web
aws s3 website s3://quest-hunter-web --index-document index.html
3

Upload files

aws s3 sync dist/ s3://quest-hunter-web --delete
4

Configure CloudFront

  1. Create CloudFront distribution
  2. Set origin to S3 bucket
  3. Configure custom error response for SPA routing
  4. Add custom domain and SSL certificate

Environment Variables

Build-Time Variables

Variables prefixed with EXPO_PUBLIC_ are bundled at build time:
EXPO_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
EXPO_PUBLIC_CONVEX_SITE_URL=https://your-deployment.convex.site
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_...
Environment variables are bundled into the JavaScript at build time. Any changes require rebuilding and redeploying the app.

Platform-Specific Configuration

For different environments (staging, production):
  1. Create environment-specific files:
    • .env.production
    • .env.staging
  2. Load the appropriate file during build:
    # Production
    cp .env.production .env && bun build
    
    # Staging
    cp .env.staging .env && bun build
    

Web-Specific Considerations

Google Maps API Key

The Google Maps API key in app.json is for Android only. For web deployment with maps, you need to configure a separate web API key.
If using Google Maps on web:
  1. Create a web-restricted API key in Google Cloud Console
  2. Add HTTP referrer restrictions
  3. Configure in your web app separately from the Android key

Location Permissions

Web browsers handle location permissions differently:
  • Users see browser permission prompt
  • HTTPS required for location access
  • Permission is per-domain
  • No persistent permission like mobile apps
Location features require HTTPS in production. Local development works over HTTP, but production deployments must use HTTPS.

PWA Configuration (Optional)

To make Quest Hunter installable as a Progressive Web App:
1

Add manifest.json

Create public/manifest.json:
{
  "short_name": "Quest Hunter",
  "name": "Quest Hunter - Location-Based Quests",
  "icons": [
    {
      "src": "favicon.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "favicon.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#E6F4FE",
  "background_color": "#ffffff"
}
2

Link manifest in HTML

Expo automatically includes the manifest, but verify in dist/index.html:
<link rel="manifest" href="/manifest.json">
3

Add service worker (optional)

For offline support, add a service worker using Workbox or similar tools.

Performance Optimization

Code Splitting

Expo’s web build automatically splits code by route when using expo-router.

Asset Optimization

  1. Images: Use expo-image which optimizes for web
  2. Fonts: Preload critical fonts
  3. Bundle Size: Analyze with:
    expo export --dump-sourcemap
    

Caching Strategy

Configure caching headers on your hosting platform:
# Static assets (JS, CSS, images)
Cache-Control: public, max-age=31536000, immutable

# HTML files
Cache-Control: public, max-age=0, must-revalidate

Testing Web Build Locally

Test the production build locally:
1

Build for production

bun build
2

Serve static files

npx serve dist
Or use any static file server:
# Python
python -m http.server 8000 --directory dist

# Node.js
npx http-server dist
3

Test in browser

Open http://localhost:8000 and verify:
  • App loads correctly
  • Routing works (try refreshing on different routes)
  • Environment variables are correct
  • External services (Convex, Clerk) connect properly

Troubleshooting

Blank Screen on Load

Common causes:
  1. Missing environment variables: Check browser console for errors
  2. CORS issues: Verify API endpoints allow your domain
  3. Build errors: Check build output for warnings

Routing Issues

If routes don’t work after refresh:
  1. Configure your hosting platform for SPA routing
  2. Ensure all requests are redirected to index.html
  3. Check netlify.toml or vercel.json configuration

Asset Loading Failures

If images or assets don’t load:
  1. Verify asset paths are relative
  2. Check build output includes all assets
  3. Ensure hosting platform serves all file types correctly

Performance Issues

  1. Large bundle size: Use source map analysis to identify large dependencies
  2. Slow load time: Enable compression (gzip/brotli) on hosting
  3. Render performance: Check React DevTools Profiler

CI/CD Automation

GitHub Actions Example

Create .github/workflows/deploy-web.yml:
name: Deploy Web

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.3.9
      
      - name: Install dependencies
        run: bun install
      
      - name: Build
        env:
          EXPO_PUBLIC_CONVEX_URL: ${{ secrets.EXPO_PUBLIC_CONVEX_URL }}
          EXPO_PUBLIC_CONVEX_SITE_URL: ${{ secrets.EXPO_PUBLIC_CONVEX_SITE_URL }}
          EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY: ${{ secrets.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY }}
        run: bun build
      
      - 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'

Next Steps

iOS Deployment

Deploy to App Store and TestFlight

Android Deployment

Deploy to Google Play Store

Build docs developers (and LLMs) love