1---
2title: Nuxt
3description: Deploy a Nuxt app to AWS with AWS.Website.Nuxt — nitro's aws-lambda preset on a streaming Lambda Function URL, S3 assets behind CloudFront, and Nuxt's own dev server under alchemy dev.
4sidebar:
5 order: 10
6---
7
8import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
9
10export const devDeps = "@alchemy.run/frontend-frameworks";
11
12`AWS.Website.Nuxt` deploys a [Nuxt](https://nuxt.com) app to AWS. It
13builds the app through your project's own `@nuxt/kit` with nitro's
14`aws-lambda` preset: the nitro server runs on a Lambda Function URL
15with response streaming, and client assets plus prerendered pages are
16served from a private S3 bucket through CloudFront. A CloudFront
17Function routes each request at the edge — uploaded files go to S3,
18everything else streams from the Lambda. There is no `nitro.preset`
19to edit and no build command to run.
20
21## Install
22
23The build integration is not bundled with alchemy. Install
24`@alchemy.run/frontend-frameworks`; the resource loads its `/nuxt` and
25`/nuxt/aws` exports from your project at deploy time. It is only used
26at build time, so a dev dependency is enough:
27
28<Tabs syncKey="pkgManager">
29 <TabItem label="bun" icon="bun">
30 <Code code={`bun add -d ${devDeps}`} lang="sh" />
31 </TabItem>
32 <TabItem label="npm" icon="npm">
33 <Code code={`npm install -D ${devDeps}`} lang="sh" />
34 </TabItem>
35 <TabItem label="pnpm" icon="pnpm">
36 <Code code={`pnpm add -D ${devDeps}`} lang="sh" />
37 </TabItem>
38 <TabItem label="yarn" icon="seti:yarn">
39 <Code code={`yarn add -D ${devDeps}`} lang="sh" />
40 </TabItem>
41</Tabs>
42
43## Configure Nuxt
44
45Your `nuxt.config.ts` loads natively — modules, layers, and all —
46so configure Nuxt exactly as you would outside Alchemy:
47
48```typescript
49// nuxt.config.ts
50export default defineNuxtConfig({
51 modules: ["@nuxtjs/tailwindcss"],
52 routeRules: {
53 "/about": { prerender: true },
54 },
55});
56```
57
58Don't set `nitro.preset` — the AWS deploy target owns the preset
59(`aws-lambda` with streaming enabled), and a foreign preset is a hard
60error.
61
62## Deploy-time overrides
63
64Config that only exists at deploy time — a stage-dependent base URL, a
65per-environment `runtimeConfig` — goes in the `nuxt` prop. It merges
66over `nuxt.config.ts` as the highest-priority layer, so a value here
67wins:
68
69```typescript
70export const Website = AWS.Website.Nuxt("Website", {
71 nuxt: {
72 app: { baseURL: "/docs/" },
73 runtimeConfig: {
74 public: { apiBase: "https://api.example.com" },
75 },
76 },
77});
78```
79
80The bag must be JSON-serializable — no functions, plugins, or modules;
81those belong in `nuxt.config.ts`. `nitro.preset` stays owned by the
82deploy target.
83
84## Declare the Website
85
86Declare the site as a module-level const (rather than inline in the
87Stack):
88
89```typescript
90// alchemy.run.ts
91import * as AWS from "alchemy/AWS";
92
93export const Website = AWS.Website.Nuxt("Website");
94```
95
96## Add it to the Stack
97
98Yield the site from your Stack and return its URL:
99
100```typescript
101// alchemy.run.ts
102import * as Alchemy from "alchemy";
103import * as Effect from "effect/Effect";
104
105export default Alchemy.Stack(
106 "MyNuxtSite",
107 {
108 providers: AWS.providers(),
109 state: AWS.state(),
110 },
111 Effect.gen(function* () {
112 const site = yield* Website;
113 return { url: site.url };
114 }),
115);
116```
117
118See
119[examples/aws-website-nuxt](https://github.com/alchemy-run/alchemy/tree/main/examples/aws-website-nuxt)
120for the checked-in example.
121
122## Add environment variables
123
124The server function's environment is configured under `env` — plain
125values, or outputs from other resources in the Stack:
126
127```diff lang="typescript"
128// alchemy.run.ts
129export const Website = AWS.Website.Nuxt("Website", {
130+ memorySize: 2048,
131+ env: {
132+ NUXT_PUBLIC_API_BASE: api.url,
133+ },
134});
135```
136
137`env` values are set on the Lambda on deploy and injected into
138the dev server's process environment under `alchemy dev`, so server
139code reads the same values in both modes. Sibling props tune the
140Lambda itself (`memorySize`, `timeout`, `architecture`, `runtime`).
141
142## Read the environment in server code
143
144On AWS the nitro server runs in a plain Node Lambda, so server routes
145and SSR read the environment from `process.env` — or, the more
146idiomatic Nuxt way, through `runtimeConfig` overridden by
147`NUXT_`-prefixed environment variables:
148
149```typescript
150// server/api/hello.ts
151export default defineEventHandler(() => {
152 return { greeting: process.env.GREETING ?? "hello" };
153});
154```
155
156## Prerendering
157
158Routes marked for prerendering in `routeRules` (or via
159`nitro.prerender`) render at build time into `.output/public` and are
160uploaded to S3, served from the edge with no Lambda invocation.
161Configure them in your project's `nuxt.config.ts`:
162
163```typescript
164// nuxt.config.ts
165export default defineNuxtConfig({
166 routeRules: {
167 "/about": { prerender: true },
168 },
169});
170```
171
172Nitro's `isr` route rule is implemented only by the Vercel and
173Netlify presets. On AWS Lambda it is silently ignored at build time,
174and the route renders on demand in the Lambda like any other SSR
175route. Use `prerender` for build-time static routes, or `cache` route
176rules for runtime caching.
177
178## Local dev
179
180```sh
181bun alchemy dev
182```
183
184`alchemy dev` runs Nuxt's own dev server (nitro dev, full HMR)
185instead of deploying anything; `site.url` is the dev server's
186`http://localhost:<port>` address and no AWS resources are created.
187Wrap the site in `Alchemy.remote()` to deploy the real infrastructure
188even during dev.
189
190## Custom domain
191
192```typescript
193const site = yield* AWS.Website.Nuxt("Web", {
194 domain: { name: "app.example.com" },
195});
196```
197
198The certificate is created in `us-east-1` (required by CloudFront),
199DNS validation records and the alias record are managed through
200Route 53. The hosted zone is inferred from the hostname — pass
201`hostedZoneId` to pin it when several zones could match.
202
203## Share a distribution with Router
204
205CloudFront distributions take minutes to create. To serve several
206sites (or a site plus an API) from one distribution, attach the site
207to an [`AWS.Website.Router`](/aws/frontend/static-site#compose-sites-with-a-router):
208
209```typescript
210const router = yield* AWS.Website.Router("FrontDoor", {
211 domain: { name: "example.com" },
212});
213
214const site = yield* AWS.Website.Nuxt("Web", {
215 domain: { router },
216});
217```