---
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.