Static
Deploy any client-side Single Page Application (SPA) or static site generator (SSG) on AWS S3 and CloudFront. Thunder’s Static construct ships with HTTPS, HTTP/3, Brotli compression, and security headers out of the box.
Supported Frameworks
- Astro (SSG mode)
- Next.js (static export)
- Vite — React, Vue, Svelte, Preact, Solid, Lit
- VitePress
- Gatsby (static)
- React Router (client-side / SSG)
- Any framework that produces a static output folder
AWS Resources
| Resource | Purpose |
|---|---|
| S3 Bucket | Stores your build output. Private, accessed only via CloudFront OAC. |
| CloudFront Distribution | Global CDN. HTTP/3, TLS 1.2+, Brotli/Gzip compression. |
| Origin Access Control | Secures S3 — no public bucket access. |
| ACM Certificate | SSL/TLS for custom domain (optional). |
| Route53 | DNS A + AAAA records for IPv4/IPv6 (optional). |
Hosting Architecture
Route 53 handles DNS resolution, directing users to the nearest CloudFront edge location. CloudFront serves as a global CDN, caching static assets at edge locations worldwide for low-latency delivery. S3 stores the application files as the origin, providing durable and scalable object storage.
Quick Start
Installation
bun add -D @thunder-so/thunderConfiguration
import { Cdk, Static, type StaticProps } from "@thunder-so/thunder";
const config: StaticProps = { env: { account: 'YOUR_ACCOUNT_ID', region: 'us-east-1' }, application: 'myapp', service: 'web', environment: 'prod', rootDir: '.', // monorepo: e.g. 'apps/web' outputDir: 'dist',};
new Static( new Cdk.App(), `${config.application}-${config.service}-${config.environment}-stack`, config);Deploy
bun run buildnpx cdk deploy --app "bunx tsx stack/prod.ts" --profile defaultCDK outputs the CloudFront URL:
Outputs:myapp-web-prod-stack.DistributionUrl = https://d1234abcd.cloudfront.netCustom Domain
Connect your own domain using Route53 and an ACM certificate. The certificate must be issued in us-east-1 — CloudFront is a global service and requires it there regardless of your app’s region.
const config: StaticProps = { // ... domain: 'app.example.com', hostedZoneId: 'Z1D633PJN98FT9', globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abc-123',};CloudFront Cache Behavior
Control what gets included in the cache key and forwarded to the origin:
const config: StaticProps = { // ... errorPagePath: '/404.html', // custom 404 page (default: /index.html) allowHeaders: ['Accept-Language'], allowCookies: ['session-*'], allowQueryParams: ['lang', 'theme'], // denyQueryParams: ['utm_source', 'fbclid'], // mutually exclusive with allowQueryParams};Lambda@Edge
URL Redirects and Rewrites
Configure URL redirects and rewrites using Lambda@Edge functions that run at CloudFront edge locations worldwide. Redirects return HTTP 301 responses to the client; rewrites transparently serve different content without changing the URL.
const config: StaticProps = { // ... redirects: [ { source: '/home', destination: '/' }, { source: '/blog/:slug', destination: '/posts/:slug' }, ], rewrites: [ { source: '/app/*', destination: '/index.html' }, // SPA fallback ],};Pattern syntax:
| Pattern | Matches |
|---|---|
/about | Exact path /about |
/blog/* | Any path starting with /blog/ |
/user/:id | /user/123, /user/abc, etc. |
/a/:x/b/:y | /a/foo/b/bar → x=foo, y=bar |
Custom Headers
Add custom HTTP response headers per path pattern. Headers run at the viewer-response stage and apply to both cached and uncached responses.
const config: StaticProps = { // ... headers: [ { path: '/assets/*', name: 'Cache-Control', value: 'public, max-age=31536000, immutable' }, { path: '/**', name: 'X-Frame-Options', value: 'SAMEORIGIN' }, ],};Path syntax:
| Path | Matches |
|---|---|
/* | Root-level paths only |
/** | All paths including nested |
/blog/* | All paths under /blog/ |
CI/CD Pipeline
AWS CodePipeline Integration
GitHub triggers the pipeline on code changes. CodePipeline orchestrates the deployment workflow, while CodeBuild executes the build process. Built assets are uploaded to S3 and the CloudFront cache is invalidated to serve the latest version.
Configuration
const config: StaticProps = { // ... accessTokenSecretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:github-token-XXXXXX', sourceProps: { owner: 'your-username', repo: 'your-repo', branchOrRef: 'main', }, buildProps: { runtime: 'nodejs', runtime_version: '22', installcmd: 'bun install', buildcmd: 'bun run build', },};Stack Outputs
| Output | Description |
|---|---|
DistributionId | CloudFront distribution ID |
DistributionUrl | CloudFront URL (https://xxxx.cloudfront.net) |
Route53Domain | Custom domain URL (only if domain is configured) |
Destroy
npx cdk destroy --app "bunx tsx stack/prod.ts" --profile default