1---
2title: Nuxt
3description: Deploy a Nuxt app to Railway with Railway.Website.Nuxt — nitro's node preset on a Service, 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`Railway.Website.Nuxt` deploys a [Nuxt](https://nuxt.com) app to
13Railway. It builds the app through your project's own `@nuxt/kit`
14with nitro's `node` preset: the nitro Node server plus prerendered
15assets run on one `Railway.Service`. Omit
16`project` to create a `Railway.Project`. The URL is
17`https://{name}.up.railway.app` unless you set `domain`. There is
18no `nitro.preset` to edit and no build command to run.
19
20## Install
21
22The build integration is not bundled with alchemy. Install
23`@alchemy.run/frontend-frameworks`; the resource loads its `/nuxt`
24and `/nuxt/node` exports from your project at deploy time. It is
25only used at build time, so a dev dependency is enough:
26
27<Tabs syncKey="pkgManager">
28 <TabItem label="bun" icon="bun">
29 <Code code={`bun add -d ${devDeps}`} lang="sh" />
30 </TabItem>
31 <TabItem label="npm" icon="npm">
32 <Code code={`npm install -D ${devDeps}`} lang="sh" />
33 </TabItem>
34 <TabItem label="pnpm" icon="pnpm">
35 <Code code={`pnpm add -D ${devDeps}`} lang="sh" />
36 </TabItem>
37 <TabItem label="yarn" icon="seti:yarn">
38 <Code code={`yarn add -D ${devDeps}`} lang="sh" />
39 </TabItem>
40</Tabs>
41
42## Configure Nuxt
43
44Your `nuxt.config.ts` loads natively — modules, layers, and all —
45so configure Nuxt exactly as you would outside Alchemy:
46
47```typescript
48// nuxt.config.ts
49export default defineNuxtConfig({
50 modules: ["@nuxtjs/tailwindcss"],
51 routeRules: {
52 "/about": { prerender: true },
53 },
54});
55```
56
57Deploy-specific overrides are merged over it via the `nuxt` prop
58(the override wins) — see [Prerendering](#prerendering) below for an
59example.
60
61Don't set `nitro.preset` — the Node deploy target owns the preset
62(`"node"`), and a foreign preset is a hard error.
63
64## Declare the Website
65
66Declare the site as a module-level const (rather than inline in the
67Stack):
68
69```typescript
70// alchemy.run.ts
71import * as Railway from "alchemy/Railway";
72
73export const Website = Railway.Website.Nuxt("Website");
74```
75
76Pass `rootDir` if `package.json` is not at `.`. Pass `project` to
77put the Service in a Project you already declared; otherwise the
78site creates `Railway.Project("Project")` under its namespace.
79
80## Add it to the Stack
81
82Yield the site from your Stack and return its URL:
83
84```typescript
85// alchemy.run.ts
86import * as Alchemy from "alchemy";
87import * as Effect from "effect/Effect";
88
89export default Alchemy.Stack(
90 "MyNuxtSite",
91 {
92 providers: Railway.providers(),
93 state: Alchemy.localState(),
94 },
95 Effect.gen(function* () {
96 const site = yield* Website;
97 return { url: site.url };
98 }),
99);
100```
101
102On deploy `site.url` is the generated `*.up.railway.app` hostname
103(or `https://{domain}` when you set `domain`). `site.service` and
104`site.project` are undefined under `alchemy dev`.
105
106## Add environment variables
107
108Process environment is the top-level `env` prop — plain strings, or
109`Redacted` values. They are copied onto `process.env` before build
110and `alchemy dev`, and onto the Service at deploy:
111
112```diff lang="typescript"
113// alchemy.run.ts
114export const Website = Railway.Website.Nuxt("Website", {
115+ env: {
116+ GREETING: "Hello from Alchemy!",
117+ NUXT_PUBLIC_API_BASE: "https://api.example.com",
118+ },
119});
120```
121
122This is the hosted Node process, not Worker bindings.
123`NUXT_`-prefixed keys override Nuxt `runtimeConfig` the usual way;
124`NUXT_PUBLIC_` values are inlined into the client at build time.
125
126## Read the environment in server code
127
128The nitro server is a plain Node process, so server routes and SSR
129read the environment from `process.env`:
130
131```typescript
132// server/api/hello.ts
133export default defineEventHandler(() => {
134 return { greeting: process.env.GREETING ?? "hello" };
135});
136```
137
138## Prerendering
139
140Routes marked for prerendering in `routeRules` (or via
141`nitro.prerender`) render at build time into `.output/public` and
142are baked into the image, served as static files before the nitro
143handler:
144
145```typescript
146export const Website = Railway.Website.Nuxt("Website", {
147 nuxt: {
148 routeRules: {
149 "/about": { prerender: true },
150 },
151 },
152});
153```
154
155Nitro's `isr` route rule is implemented only by the Vercel and
156Netlify presets. On the Node preset it is silently ignored at build
157time, and the route renders on demand like any other SSR route. Use
158`prerender` for build-time static routes, or `cache` route rules for
159runtime caching.
160
161## Local dev
162
163```sh
164bun alchemy dev
165```
166
167`alchemy dev` runs Nuxt's own dev server (nitro dev, full HMR)
168instead of deploying; `site.url` is the local address and no
169Railway resources are created. Wrap the site in `Alchemy.remote()` to deploy the live Project and
170Service even during dev.
171
172## Custom domain
173
174`domain` is a hostname string. The site attaches a
175[`Railway.CustomDomain`](/railway/networking#custom-domains)
176(`targetPort` 3000) and `url` becomes `https://{domain}` instead of
177the generated `*.up.railway.app`:
178
179```typescript
180export const Website = Railway.Website.Nuxt("Website", {
181 domain: "app.example.com",
182});
183```
184
185Point DNS at Railway's verification records on the CustomDomain.
186
187## Where next
188
189- [`Railway.Website.Nuxt` reference](/providers/railway/website/nuxt)
190 — every prop and attribute.
191- [Services](/railway/compute/services) and
192 [Projects](/railway/compute/projects) — the Service and Project
193 the site creates.
194- [Custom domains](/railway/networking#custom-domains) — hostnames
195 on a Service.
196- [Setup](/railway/setup) — workspace, API token, and alchemy
197 login.