Deploy React Router on AWS
Declarative routing for React with support for SSR and static exports.
reactrouter.comDeploy your React Router applications to AWS using Thunder patterns. This guide covers client-side rendering (CSR), static pre-rendering, and full-stack server-side rendering options.
There are two deployment patterns available for React Router on AWS:
Getting Started
Create Project
npm create vite@latest my-react-router-app -- --template react-tscd my-react-router-appnpm install react-router-dom@7pnpm create vite my-react-router-app --template react-tscd my-react-router-apppnpm add react-router-dom@7bun create vite my-react-router-app --template react-tscd my-react-router-appbun add react-router-dom@7Single Page Application (SPA) Deployment
Deploy React Router applications to S3 and CloudFront using the Static construct. This pattern supports both client-side rendering (CSR) and static pre-rendering, offering flexibility for different use cases.
Configure React Router
Choose one of the following configurations based on your needs:
Client-Side Rendering (CSR) — Routes are always client-side rendered as users navigate:
import type { Config } from "@react-router/dev/config";
export default { ssr: false,} satisfies Config;Static Pre-rendering — Generate static HTML at build time for specific routes:
import type { Config } from "@react-router/dev/config";
export default { // Return a list of URLs to prerender at build time async prerender() { return ["/", "/about", "/contact"]; },} satisfies Config;Pre-rendering generates static HTML and client navigation data payloads for a list of URLs, offering better performance and SEO without requiring a server. Route module loaders are used to fetch data at build time. Individual routes can also use client data loading with clientLoader to supplement pre-rendered data.
Stack
import { Cdk, Static, type StaticProps } from "@thunder-so/thunder";
const myApp: StaticProps = { env: { account: 'your-account-id', region: 'us-east-1' }, application: 'your-application-id', service: 'your-service-id', environment: 'production',
rootDir: '', // e.g. 'frontend' for monorepos outputDir: 'dist',};
new Static( new Cdk.App(), `${myApp.application}-${myApp.service}-${myApp.environment}-stack`, myApp);Deploy
Build and deploy your React Router SPA:
npm run buildnpx cdk deploy --app "npx tsx stack/prod.ts"pnpm run buildpnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts"bun run buildnpx cdk deploy --app "bunx tsx stack/prod.ts"After deployment, you’ll receive a CloudFront URL to access your application.
Full Stack Deployment (SSR)
Deploy server-side rendered React Router applications using ECS Fargate and Application Load Balancer with the Fargate construct.
Configure React Router for SSR
import type { Config } from "@react-router/dev/config";
export default { ssr: true,} satisfies Config;Server-side rendering requires a deployment that supports it. Individual routes can still be statically pre-rendered, and routes can also use client data loading with clientLoader to avoid server rendering/fetching for their portion of the UI.
Stack
import { Cdk, Fargate, type FargateProps } from "@thunder-so/thunder";
const svcProps: FargateProps = { env: { account: 'your-account-id', region: 'us-east-1' }, application: 'your-application-id', service: 'your-service-id', environment: 'production',
rootDir: '', serviceProps: { architecture: Cdk.aws_ecs.CpuArchitecture.ARM64, cpu: 512, memorySize: 1024, port: 3000, desiredCount: 1, healthCheckPath: '/', },};
new Fargate( new Cdk.App(), `${svcProps.application}-${svcProps.service}-${svcProps.environment}-stack`, svcProps);Build Settings Using Nixpacks
Configure automatic containerization with Nixpacks:
const svcProps: FargateProps = { // ... other props
buildProps: { buildSystem: 'Nixpacks', installcmd: 'bun install', buildcmd: 'bun run build', startcmd: 'bun start', },};Build Settings Using Docker Container
Alternatively, use a custom Dockerfile:
FROM oven/bun:latest AS builderWORKDIR /appCOPY package.json bun.lockb ./RUN bun install --frozen-lockfileCOPY . .RUN bun run build
FROM oven/bun:latest AS runnerWORKDIR /appENV NODE_ENV=productionENV HOST=0.0.0.0ENV PORT=3000COPY --from=builder /app/build ./buildEXPOSE 3000CMD ["bun", "run", "./build/server/index.js"]const svcProps: FargateProps = { // ... other props
serviceProps: { dockerFile: 'Dockerfile', port: 3000, },};Environment Variables and Secrets for SSR
Configure runtime environment variables and secrets:
const svcProps: FargateProps = { // ... other props
serviceProps: { variables: [ { NODE_ENV: 'production' }, { VITE_API_URL: 'https://api.example.com' } ], secrets: [ { key: 'DATABASE_URL', resource: 'arn:aws:secretsmanager:us-west-2:123456789012:secret:/my-app/DATABASE_URL-abc123' }, ], },};Deploy
Build and deploy your containerized application:
npm run buildnpx cdk deploy --app "npx tsx stack/prod.ts"pnpm run buildpnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts"bun run buildnpx cdk deploy --app "bunx tsx stack/prod.ts"After deployment, you’ll receive an Application Load Balancer URL to access your SSR application.