Skip to content

Websites

Alchemy deploys frontends to Fly with a family of Fly.Website resources. Each one builds your project programmatically and deploys it as a Node Service on a Fly App — a Machine listening on port 3000, plus a shared IPv4 so https://{app}.fly.dev answers — with no fly.toml and no adapter to put in the framework config. Your framework’s own config file (vite.config.ts, astro.config.*, nuxt.config.ts, …) loads natively; Alchemy layers its Node container integration on top.

  • Vite — client-only Vite apps (React, Vue, Solid SPAs, index.html multi-page sites); a tiny static-file server serves the vite build output.
  • Astro — Astro sites, server-rendered on Node or fully static; your astro.config.* loads natively.
  • Nextjs — Next.js as a long-running Node process (next build, then next({ dev: false })). Not OpenNext.
  • Nuxt — Nuxt apps through nitro’s Node server; your nuxt.config.ts loads natively. Do not set nitro.preset.
  • ReactRouter — React Router v7 in framework mode, built through your own vite build.
  • SolidStart — SolidStart 2.x SSR; Alchemy appends nitro’s node preset to your vite build.
  • SvelteKit — SvelteKit SSR plus prerendered assets; the Node adapter is injected. Do not set kit.adapter.
  • TanStackStart — TanStack Start (React or Solid), built through your own vite build.
  • Waku — Waku (React Server Components) on Node, with SSG pages served extensionless.
  • Octane — OctaneJS SSR. Select node() from @alchemy.run/frontend-frameworks/octane/node-adapter in octane.config.ts.
  • Foldkit — client-only Foldkit SPA; deep links fall back to index.html.
  • Vocs — prerendered Vocs docs; extensionless pages (/aboutabout/index.html).
  • StaticSite — any build command’s output directory (Hugo, Zola, Eleventy).

Each resource creates a Fly.App when you omit app, a Fly.Service on port 3000, and a shared_v4 IpAssignment so {app}.fly.dev answers. domain requests ACME via Certificate (existing DNS only for v1); url becomes https://{domain}. The return is { url, app, service, ip, certificate } — everything except url is undefined under alchemy dev.

Shared props: rootDir, memo, env (Machine process environment — not Worker bindings), dev, domain, app. Builds are memoized by content-hashing the input files — an unchanged project skips the build.

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

See examples/fly-website-vite for a Vite SPA next to a Fly Service API.

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

Use Vite when the whole deployable output is static assets — a React, Vue, or Solid SPA, or an index.html-per-route multi-page app. SSR frameworks that wrap Vite (Astro, SvelteKit, Octane, …) have their own resources; Vite never starts a framework server module.

Use StaticSite when the build is an arbitrary shell command that emits a directory of files.

Top-level env is copied onto process.env before build and dev, and onto the Machine at deploy. Client inlining is whatever the framework already does (VITE_* for Vite). It is not Worker bindings and not AWS server.environment.

const site = yield* Fly.Website.Vite("Web", {
env: {
VITE_API_URL: api.url,
},
});

Server code reads process.env. Pair a site with Postgres the same way you would any other Service: put the connection string in env, or bind ConnectPostgres on a sibling Service.

alchemy dev runs the framework’s own dev server (native HMR) instead of deploying. site.url is the local address and no Fly App, Service, IP, or certificate is created. Wrap the site in Alchemy.remote() to deploy the live path even during dev.

const site = yield* Fly.Website.Astro("Web", {
domain: "app.example.com",
});

domain is a hostname string. Alchemy requests ACME on the App; point existing DNS at the App first (v1 does not create records). url becomes https://app.example.com.