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
- 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
- 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
- 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
2
3
Test Development Server
Start the development server:w to open in web browser and verify routing works.4
5
Test Production Build Locally
Serve the dist directory:http://localhost:3000 to test the static site.Dynamic Routes and Static Generation
Static export can generate pages for dynamic routes usinggenerateStaticParams.
Generating Static Params
For dynamic routes like app/blog/[slug].tsx, define which pages to generate:app/blog/[slug].tsx
Cascading Parameters
Nested dynamic routes receive parent parameters:app/blog/[category]/[slug].tsx
Reading Local Files
Use Node.js APIs ingenerateStaticParams:
app/docs/[page].tsx
Customizing HTML Output
Root HTML Template
Customize the base HTML for all pages:app/+html.tsx
- 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: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
SEO Optimization
Sitemap Generation
Create public/sitemap.xml:public/sitemap.xml
scripts/generate-sitemap.js
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: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
- 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:- Rebuild and redeploy when content changes
- Client-side fetching for real-time updates
- 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:-
Export function correctly:
-
Check web output mode:
app.json
Assets not loading
Issue: Images/fonts return 404 Solutions:-
Use absolute paths:
-
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 ingenerateStaticParams:
Next Steps
- Hosting Providers - Deploy your static export
- Over-the-Air Updates - Update native apps remotely
- Build Configuration - Configure builds for mobile