Skip to content

Vite

Neon.Website.Vite builds a Vite app and packages its static assets with a Node 24 Fetch handler in a Neon Function ZIP. It does not provision a container, start a listening Bun server, or upload the site to S3. For SSR, use the framework-specific constructors instead.

Start with an existing Vite app or the runnable Vite example. Keep your plugins in vite.config.*. From the app directory:

Install Alchemy and its matching Effect dependencies first; use Node 24 for this build toolchain.

Terminal window
pnpm add -D @alchemy.run/frontend-frameworks @vercel/nft

The integration drives the build; @vercel/nft traces runtime dependencies for packaging. Configure your deployment account key using Neon setup. In CI, set CI=true and NEON_API_KEY in the deployment environment, not the website’s env.

alchemy.run.ts
import * as Alchemy from "alchemy";
import * as Neon from "alchemy/Neon";
import * as Effect from "effect/Effect";
export default Alchemy.Stack(
"NeonVite",
{ providers: Neon.providers(), state: Alchemy.localState() },
Effect.gen(function* () {
const site = yield* Neon.Website.Vite("Web", {
rootDir: ".",
});
return { url: site.url };
}),
);

An omitted scope creates an owned Ohio (aws-us-east-2) Project on live deploy, then a Function on its default branch. site.function exposes the Function; site.url is its URL. Keep the local state files for subsequent deploys and cleanup.

Vite defaults to SPA routing: unmatched GET/HEAD paths fall back to index.html. For a multi-page build that emits 404.html, change only the asset fallback:

const site = yield* Neon.Website.Vite("Web", {
rootDir: ".",
assets: { notFoundHandling: "404-page" },
});

The vite prop accepts serializable outDir and base overrides. Keep plugin instances in vite.config.*, not in resource props.

Terminal window
pnpm exec alchemy dev

This runs Vite’s native dev server and HMR. The site returns a local URL with function and domain undefined; it creates no implicit Neon Project. Any explicit backend resources you declare separately still have their own lifecycle.

Apply Alchemy.remote() to the site to deploy it even during alchemy dev:

const site = yield* Neon.Website.Vite("Web", {
rootDir: ".",
});
}).pipe(Alchemy.remote());

This requires cloud credentials and incurs the normal live resource lifecycle.

Terminal window
pnpm exec alchemy deploy
pnpm exec alchemy destroy

Run destroy when finished, from the same directory and with the same stage and state. It removes the site’s owned resources, including an implicit Project.

All 13 Neon Website constructors accept either project or branch, never both. A project selects its current default branch; a branch pins an explicit identity. For an existing branch, add just the scope to the site props:

const site = yield* Neon.Website.Vite("Web", {
rootDir: ".",
branch: { projectId: "your-project-id", branchId: "your-branch-id" },
});

Replace both IDs with your own. Referenced projects and branches are not owned by the Website. When declaring a Project for a combined website/storage backend, choose region: "aws-us-east-2" (Ohio) and verify service availability for your account.

The deployment account key is not an application credential. Neon.Credential grants branch-and-descendant permissions, not per-bucket permissions. Request only needed scopes; storage reads currently require explicit storage:read, even with storage:write. Never put account keys or privileged service tokens in a browser bundle.

The website’s env is supplied to build, native dev, and Function configuration. Vite embeds VITE_* values into browser assets; there is no Vite server application to read private runtime values. Redacted does not prevent a build tool from embedding secrets. SSR constructors use the same shared environment contract, with their own framework’s public-variable rules.

Static files are served inside the Function, currently with Cache-Control: no-cache; this composition adds no CDN or durable framework cache. The domain prop registers a custom hostname and exposes site.domain.cnameTarget for a DNS-only CNAME. Registration and the returned URL do not establish DNS propagation or valid TLS; verify HTTPS separately. Follow Custom domains for DNS, CAA, HTTPS checks, and safe retirement, or see the CustomDomain reference.