Deploy React Router on AWS
Deploy 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:
- Single Page Application (SPA) — Deploy client-side or static pre-rendered React Router sites using S3 and CloudFront with the
Staticconstruct - Full Stack — Deploy SSR React Router applications using ECS Fargate with the
Fargateconstruct
Single 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.
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@7Configure 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.
Install Dependencies and Setup Stack
npm i tsx @thunder-so/thunder --save-devpnpm add -D tsx @thunder-so/thunderbun add -d tsx @thunder-so/thunderimport { 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 --all --app="npx tsx stack/index.ts"pnpm run buildpnpm exec cdk deploy --all --app="pnpm exec tsx stack/index.ts"bun run buildnpx cdk deploy --all --app="bunx tsx stack/index.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.
Install Dependencies and Setup Stack
npm i tsx @thunder-so/thunder --save-devpnpm add -D tsx @thunder-so/thunderbun add -d tsx @thunder-so/thunderimport { Cdk, Fargate, type FargateProps } from "@thunder-so/thunder";
const svcProps: FargateProps = { env: { account: 'your-account-id', region: 'us-west-2' }, application: 'your-application-id', service: 'your-service-id', environment: 'production',
rootDir: '', // e.g. 'app' for monorepos};
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 public.ecr.aws/docker/library/node:20-alpine AS base
FROM base AS builderWORKDIR /app
COPY package*.json ./RUN npm ci
COPY . .RUN npm run build
FROM base AS runnerWORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/build ./build
EXPOSE 3000
CMD ["node", "./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 --all --app="npx tsx stack/index.ts"pnpm run buildpnpm exec cdk deploy --all --app="pnpm exec tsx stack/index.ts"bun run buildnpx cdk deploy --all --app="bunx tsx stack/index.ts"After deployment, you’ll receive an Application Load Balancer URL to access your SSR application.