Skip to main content

Static Export for Web

Static export generates pre-rendered HTML files for your Expo Router web app, enabling SEO optimization and deployment to static hosting providers. This is ideal for content-focused apps, marketing sites, and apps that don’t require server-side rendering.

Understanding Web Output Modes

Expo Router supports three web output modes:

Single-Page Application (SPA)

Default mode - Generates one index.html file:
app.json
Use when:
  • SEO is not important
  • Client-side routing only
  • Simplest deployment

Static Site Generation (SSG)

Recommended for most apps - Pre-renders HTML for each route:
app.json
Use when:
  • SEO is important
  • Multiple pages with unique content
  • Want fast initial page loads
  • Can deploy to static hosting (Netlify, Vercel, etc.)

Server-Side Rendering (SSR)

Advanced mode - Renders pages on each request:
app.json
Use when:
  • Need server functions or API routes
  • Dynamic data on each request
  • User-specific content
  • Requires server deployment (not covered in this guide)

Setting Up Static Export

1

Enable Static Output

Configure your app.json:
app.json
2

Create Routes

Expo Router automatically generates pages from your app directory:
3

Test Development Server

Start the development server:
Press w to open in web browser and verify routing works.
4

Export Static Files

Generate production build:
This creates a dist directory with:
5

Test Production Build Locally

Serve the dist directory:
Open http://localhost:3000 to test the static site.

Dynamic Routes and Static Generation

Static export can generate pages for dynamic routes using generateStaticParams.

Generating Static Params

For dynamic routes like app/blog/[slug].tsx, define which pages to generate:
app/blog/[slug].tsx
Output:

Cascading Parameters

Nested dynamic routes receive parent parameters:
app/blog/[category]/[slug].tsx

Reading Local Files

Use Node.js APIs in generateStaticParams:
app/docs/[page].tsx
Do not use __dirname in generateStaticParams - it points to the build directory, not your source. Always use process.cwd().

Customizing HTML Output

Root HTML Template

Customize the base HTML for all pages:
app/+html.tsx
Important notes:
  • This file only runs in Node.js during export
  • Cannot import global CSS here (use root layout instead)
  • No access to browser APIs (window, document, etc.)
  • Children prop includes root <div id="root" />

Page-Specific Meta Tags

Add meta tags per route:
app/blog/[slug].tsx

Static Assets

Public Directory

Files in public/ are copied to dist/ root:
After export:

Referencing Static Assets

Access public files from root path:

Asset Optimization

Optimize images before placing in public/:

Font Loading

Expo Font automatically optimizes font loading for static sites:
app/_layout.tsx
Generated HTML includes:
Fonts are preloaded and available immediately, preventing layout shift.

SEO Optimization

Sitemap Generation

Create public/sitemap.xml:
public/sitemap.xml
Or generate dynamically during build:
scripts/generate-sitemap.js
Run after export:
package.json

Robots.txt

Create public/robots.txt:
public/robots.txt

Structured Data

Add JSON-LD structured data:
app/blog/[slug].tsx

Performance Optimization

Code Splitting

Automatic code splitting by route:
Manual code splitting with dynamic imports:

Image Optimization

Use responsive images:

Preloading Critical Resources

Add resource hints:
app/+html.tsx

Limitations

No Server-Side Logic

Static export cannot:
  • Run server functions
  • Execute API routes (+api.ts files)
  • Render pages dynamically per request
  • Access request headers or cookies
  • Handle form submissions server-side
Workarounds:
  • Use client-side API calls
  • Integrate with serverless functions (separate from static export)
  • Use EAS Hosting for server output mode

Dynamic Routes Require Pre-Generation

All dynamic routes must be defined at build time:

No Real-Time Data

Static pages show data from build time. For fresh data:
  1. Rebuild and redeploy when content changes
  2. Client-side fetching for real-time updates
  3. Incremental Static Regeneration (requires server output mode)

Troubleshooting

”No routes found”

Issue: Export creates empty dist folder Solution: Verify app/ directory structure:

“generateStaticParams not called”

Issue: Dynamic pages not generated Solutions:
  1. Export function correctly:
  2. Check web output mode:
    app.json

Assets not loading

Issue: Images/fonts return 404 Solutions:
  1. Use absolute paths:
  2. Place in public directory:

Build fails with “process is not defined”

Issue: Using Node.js APIs in client code Solution: Only use Node.js APIs in generateStaticParams:

Next Steps