--- title: 'Deploy React Router on AWS' description: 'Deploy React Router applications on AWS using CSR/static pre-rendering with S3 and CloudFront, or full-stack SSR with ECS Fargate and Application Load Balancer.' --- import { Tabs, TabItem, LinkButton, Aside } from '@astrojs/starlight/components'; import PatternList from '../../../components/docs/PatternList.astro'; import FrameworkHero from '../../../components/docs/FrameworkHero.astro'; Deploy your [React Router](https://reactrouter.com/) 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 ```sh npm create vite@latest my-react-router-app -- --template react-ts cd my-react-router-app npm install react-router-dom@7 ``` ```sh pnpm create vite my-react-router-app --template react-ts cd my-react-router-app pnpm add react-router-dom@7 ``` ```sh bun create vite my-react-router-app --template react-ts cd my-react-router-app bun add react-router-dom@7 ``` ## 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. ### 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: ```ts title="react-router.config.ts" 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: ```ts title="react-router.config.ts" 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 ```ts title="stack/prod.ts" 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: ```sh npm run build npx cdk deploy --app "npx tsx stack/prod.ts" ``` ```sh pnpm run build pnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" ``` ```sh bun run build npx 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 ```ts title="react-router.config.ts" 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 ```ts title="stack/prod.ts" 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: ```ts title="stack/prod.ts" 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: ```dockerfile title="Dockerfile" FROM oven/bun:latest AS builder WORKDIR /app COPY package.json bun.lockb ./ RUN bun install --frozen-lockfile COPY . . RUN bun run build FROM oven/bun:latest AS runner WORKDIR /app ENV NODE_ENV=production ENV HOST=0.0.0.0 ENV PORT=3000 COPY --from=builder /app/build ./build EXPOSE 3000 CMD ["bun", "run", "./build/server/index.js"] ``` ```ts title="stack/prod.ts" const svcProps: FargateProps = { // ... other props serviceProps: { dockerFile: 'Dockerfile', port: 3000, }, }; ``` ### Environment Variables and Secrets for SSR Configure runtime environment variables and secrets: ```ts title="stack/prod.ts" 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: ```sh npm run build npx cdk deploy --app "npx tsx stack/prod.ts" ``` ```sh pnpm run build pnpm exec cdk deploy --app "pnpm exec tsx stack/prod.ts" ``` ```sh bun run build npx cdk deploy --app "bunx tsx stack/prod.ts" ``` After deployment, you'll receive an **Application Load Balancer URL** to access your SSR application.