1---
2title: SvelteKit
3description: Deploy a SvelteKit app to AWS with AWS.Website.SvelteKit — SSR on a streaming Lambda Function URL, assets and prerendered pages on S3 + CloudFront, and Kit's own dev server under alchemy dev.
4sidebar:
5 order: 12
6---
7
8import { Code, Tabs, TabItem } from "@astrojs/starlight/components";
9
10export const devDeps = "@alchemy.run/frontend-frameworks";
11
12`AWS.Website.SvelteKit` deploys a
13[SvelteKit](https://svelte.dev/docs/kit) app to AWS. It builds the
14app with SvelteKit's own Vite pipeline and a wrangler-free in-memory
15AWS adapter emitting a streaming Lambda handler. Client assets and
16prerendered pages are served from a private S3 bucket through
17CloudFront; dynamic routes stream from the Lambda Function URL. Your
18`vite.config.ts` loads natively; there is no `svelte.config.js` to
19write (kit v3 dropped it) and no adapter to install.
20
21## Install
22
23The build integration is not bundled with alchemy. Install
24`@alchemy.run/frontend-frameworks`; the resource loads its
25`/sveltekit` and `/sveltekit/aws` exports from your project at deploy
26time. It is only used at 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 SvelteKit
44
45Your project's `vite.config.ts` loads natively — your Vite plugins
46and the kit options in your `sveltekit(...)` call all apply as usual.
47Alchemy injects its own wrangler-free AWS adapter (replacing any
48adapter you declare, with a warning), so a fresh SvelteKit project
49deploys as-is. Deploy-specific kit overrides can also be passed via
50the `kit` prop, which merges over your `sveltekit(...)` options — see
51[Kit options](#kit-options) below. A project without a
52`vite.config.*` works too: the resource falls back to a fully
53programmatic build.
54
55## Declare the Website
56
57Declare the site as a module-level const (rather than inline in the
58Stack):
59
60```typescript
61// alchemy.run.ts
62import * as AWS from "alchemy/AWS";
63
64export const Website = AWS.Website.SvelteKit("Website");
65```
66
67## Add it to the Stack
68
69Yield the site from your Stack and return its URL:
70
71```typescript
72// alchemy.run.ts
73import * as Alchemy from "alchemy";
74import * as Effect from "effect/Effect";
75
76export default Alchemy.Stack(
77 "MySvelteKitSite",
78 {
79 providers: AWS.providers(),
80 state: AWS.state(),
81 },
82 Effect.gen(function* () {
83 const site = yield* Website;
84 return { url: site.url };
85 }),
86);
87```
88
89Routes with `export const prerender = true` are uploaded to S3 and
90served from the edge without invoking the server; server routes
91(`+server.ts`) and server load functions stream from the Lambda.
92
93See
94[examples/aws-website-sveltekit](https://github.com/alchemy-run/alchemy/tree/main/examples/aws-website-sveltekit)
95for the checked-in example.
96
97## Add environment variables
98
99The server function's environment is configured under `env` — plain
100values, or outputs from other resources in the Stack:
101
102```diff lang="typescript"
103// alchemy.run.ts
104export const Website = AWS.Website.SvelteKit("Website", {
105+ env: {
106+ GREETING: "Hello from alchemy",
107+ API_BASE: api.url,
108+ },
109});
110```
111
112The values are set on the Lambda at deploy time and injected into the
113dev server's process environment under `alchemy dev`, so server code
114reads the same values in both modes.
115
116## Read the environment in server code
117
118The Kit server runs in a plain Node Lambda, so server routes read the
119environment from `process.env` (or kit's `$env/dynamic/private`,
120which is initialized from it):
121
122```typescript
123// src/routes/+page.server.ts
124export const load = () => {
125 return { greeting: process.env.GREETING ?? "hello" };
126};
127```
128
129## Kit options
130
131Since kit v3 there is no `svelte.config.js` — kit options live in the
132`sveltekit(...)` call in your `vite.config.ts`, which loads natively:
133
134```typescript
135// vite.config.ts
136import { sveltekit } from "@sveltejs/kit/vite";
137import tailwindcss from "@tailwindcss/vite";
138import { defineConfig } from "vite";
139
140export default defineConfig({
141 plugins: [
142 tailwindcss(),
143 sveltekit({
144 alias: { $lib: "src/lib" },
145 }),
146 ],
147});
148```
149
150The `kit` prop on the resource is the deploy-time override bag:
151JSON-serializable kit config merged over those options (the prop
152wins). Use it for per-stage values or alchemy `Output`s your config
153file can't compute:
154
155```typescript
156// alchemy.run.ts
157export const Website = AWS.Website.SvelteKit("Website", {
158 kit: {
159 paths: { base: "/docs" },
160 },
161});
162```
163
164No functions or plugins in the bag — `preprocess`, `vitePlugin`, and
165the other construction-time options belong in your `sveltekit(...)`
166call. Don't set `kit.adapter` — Alchemy injects its own wrangler-free
167AWS adapter (an adapter declared in your `sveltekit(...)` call is
168replaced with a warning).
169
170## Local dev
171
172```sh
173bun alchemy dev
174```
175
176`alchemy dev` runs SvelteKit's own Vite dev server — Node SSR with
177full HMR, which is already the AWS Lambda programming model
178(`process.env` included) — instead of deploying; `site.url` is the
179local address and no AWS resources are created. Wrap the site in
180`Alchemy.remote()` to deploy the real infrastructure even during dev.
181
182## Custom domain
183
184```typescript
185const site = yield* AWS.Website.SvelteKit("Web", {
186 domain: { name: "app.example.com" },
187});
188```
189
190The hosted zone is inferred from the hostname. Pass `hostedZoneId` to
191pin it when several zones could match.