Skip to content

Websites

A website on AWS is three pieces working together. Its static files, the scripts, styles and images, sit in a private S3 Bucket. Its server, if it has one, runs on a Lambda Web Function. And a CloudFront distribution in front of both serves every request from a location near the visitor: files straight from the Bucket, everything else from the server.

/assets/*everything elsevisitorapp.example.comCloudFrontedge routerAssetsS3 BucketServerLambda Web Function
CloudFront serves files from the Bucket and sends the rest to the server.

Each framework has a Website resource that builds your project and creates all three. Your framework’s own config file loads as it is, with nothing AWS-specific in it:

alchemy.run.ts
const site = yield* AWS.Website.Astro("Website", { domain: "app.example.com" });

Every Website resource takes the same props: a domain, with its certificate and DNS records (see Custom domains), the server’s timeout, env and scaling, and how assets are uploaded and cached. A build is skipped when no input file has changed. Under alchemy dev, each one runs the framework’s own dev server instead of deploying.

A site with no server, a single-page app or a static generator’s output, creates no Function at all: just the Bucket and the distribution.

List extra Regions to run the server in each of them behind one global domain. CloudFront forwards to it, and each request reaches the nearest Region:

const site = yield* AWS.Website.Nuxt("Website", {
regions: ["us-east-1", "eu-west-1"],
});

The site’s URL doesn’t change when you add or remove Regions. Nextjs and Vinext keep their data caches in the home Region, so a server in another Region reaches back to it for cached pages; regions helps most for sites without a server-side cache.

The SSR server runs on a Lambda Web Function: your framework’s own Node server, serving many requests per environment and streaming each response. Web Functions run in a subset of Regions. Elsewhere, set server: "function" to run the server on a Lambda Function behind a streaming Function URL:

const site = yield* AWS.Website.Nuxt("Website", {
server: "function",
memorySize: 2048,
});

memorySize, architecture, and runtime apply only to server: "function"; a Web Function has a fixed 2 GB, Arm64 and Node.js 24.

Framework Resource Guide
React / Vite SPA Vite React SPA
Vue Vite Vue
Foldkit Foldkit Foldkit
TanStack Start (React & Solid) TanStackStart TanStack Start
React Router ReactRouter React Router
Astro Astro Astro
Next.js Nextjs Next.js
vinext Vinext vinext
Nuxt Nuxt Nuxt
SvelteKit SvelteKit SvelteKit
Waku Waku Waku
OctaneJS (fullstack) Octane Octane
Zola, Hugo, or any static generator StaticSite Static sites

Every row is backed by a checked-in example or a live deploy test in the Alchemy repository. The last row is deliberately open-ended: StaticSite deploys any directory of files (running your build command first if you give it one), so any generator works the same way.

Use the resource named after your framework. Astro, Nextjs, Nuxt, ReactRouter, SvelteKit, TanStackStart, Waku, and Octane each drive their framework’s own build and know its output layout, config surface, and dev server.

Use Vite when the app is client-only and a single vite build produces the whole thing — a React or Vue SPA, or any Vite project with no server. It creates no Lambda.

Use StaticSite when the output is plain files from an arbitrary build command that emits a directory (Zola, Hugo, or any other generator without a dedicated resource).

Use Router when several sites (or a site plus an API) should share one CloudFront distribution and one domain — distributions take minutes to create, and each custom domain can only attach to one.