| 1 | --- |
| 2 | title: Vite |
| 3 | description: Package a Vite client build as a Neon Function, with native Vite development. |
| 4 | sidebar: |
| 5 | order: 1 |
| 6 | --- |
| 7 | |
| 8 | `Neon.Website.Vite` builds a Vite app and packages its static assets with a |
| 9 | Node 24 Fetch handler in a Neon Function ZIP. It does not provision a container, |
| 10 | start a listening Bun server, or upload the site to S3. For SSR, use the |
| 11 | [framework-specific constructors](/neon/frontend/astro) instead. |
| 12 | |
| 13 | ## Prepare the app |
| 14 | |
| 15 | Start with an existing Vite app or the |
| 16 | [runnable Vite example](https://github.com/alchemy-run/alchemy/tree/main/examples/neon-website-vite). |
| 17 | Keep your plugins in `vite.config.*`. From the app directory: |
| 18 | |
| 19 | Install [Alchemy and its matching Effect dependencies](/getting-started) first; |
| 20 | use Node 24 for this build toolchain. |
| 21 | |
| 22 | ```sh |
| 23 | pnpm add -D @alchemy.run/frontend-frameworks @vercel/nft |
| 24 | ``` |
| 25 | |
| 26 | The integration drives the build; `@vercel/nft` traces runtime dependencies for |
| 27 | packaging. Configure your deployment account key using [Neon setup](/neon/setup). |
| 28 | In CI, set `CI=true` and `NEON_API_KEY` in the deployment environment, not the |
| 29 | website's `env`. |
| 30 | |
| 31 | ## Define the stack |
| 32 | |
| 33 | ```typescript |
| 34 | // alchemy.run.ts |
| 35 | import * as Alchemy from "alchemy"; |
| 36 | import * as Neon from "alchemy/Neon"; |
| 37 | import * as Effect from "effect/Effect"; |
| 38 | |
| 39 | export default Alchemy.Stack( |
| 40 | "NeonVite", |
| 41 | { providers: Neon.providers(), state: Alchemy.localState() }, |
| 42 | Effect.gen(function* () { |
| 43 | const site = yield* Neon.Website.Vite("Web", { |
| 44 | rootDir: ".", |
| 45 | }); |
| 46 | return { url: site.url }; |
| 47 | }), |
| 48 | ); |
| 49 | ``` |
| 50 | |
| 51 | An omitted scope creates an owned Ohio (`aws-us-east-2`) Project on live deploy, |
| 52 | then a Function on its default branch. `site.function` exposes the Function; |
| 53 | `site.url` is its URL. Keep the local state files for subsequent deploys and cleanup. |
| 54 | |
| 55 | ## Choose routing |
| 56 | |
| 57 | Vite defaults to SPA routing: unmatched GET/HEAD paths fall back to `index.html`. |
| 58 | For a multi-page build that emits `404.html`, change only the asset fallback: |
| 59 | |
| 60 | ```diff lang="typescript" |
| 61 | const site = yield* Neon.Website.Vite("Web", { |
| 62 | rootDir: ".", |
| 63 | + assets: { notFoundHandling: "404-page" }, |
| 64 | }); |
| 65 | ``` |
| 66 | |
| 67 | The `vite` prop accepts serializable `outDir` and `base` overrides. Keep plugin |
| 68 | instances in `vite.config.*`, not in resource props. |
| 69 | |
| 70 | ## Develop locally |
| 71 | |
| 72 | ```sh |
| 73 | pnpm exec alchemy dev |
| 74 | ``` |
| 75 | |
| 76 | This runs Vite's native dev server and HMR. The site returns a local URL with |
| 77 | `function` and `domain` undefined; it creates no implicit Neon Project. Any |
| 78 | explicit backend resources you declare separately still have their own lifecycle. |
| 79 | |
| 80 | ## Opt out of local development |
| 81 | |
| 82 | Apply `Alchemy.remote()` to the site to deploy it even during `alchemy dev`: |
| 83 | |
| 84 | ```diff lang="typescript" |
| 85 | const site = yield* Neon.Website.Vite("Web", { |
| 86 | rootDir: ".", |
| 87 | - }); |
| 88 | + }).pipe(Alchemy.remote()); |
| 89 | ``` |
| 90 | |
| 91 | This requires cloud credentials and incurs the normal live resource lifecycle. |
| 92 | |
| 93 | ## Deploy and remove |
| 94 | |
| 95 | ```sh |
| 96 | pnpm exec alchemy deploy |
| 97 | pnpm exec alchemy destroy |
| 98 | ``` |
| 99 | |
| 100 | Run `destroy` when finished, from the same directory and with the same stage and |
| 101 | state. It removes the site's owned resources, including an implicit Project. |
| 102 | |
| 103 | ## Scope and credentials |
| 104 | |
| 105 | All 13 Neon Website constructors accept either `project` or `branch`, never both. |
| 106 | A project selects its current default branch; a branch pins an explicit identity. |
| 107 | For an existing branch, add just the scope to the site props: |
| 108 | |
| 109 | ```diff lang="typescript" |
| 110 | const site = yield* Neon.Website.Vite("Web", { |
| 111 | rootDir: ".", |
| 112 | + branch: { projectId: "your-project-id", branchId: "your-branch-id" }, |
| 113 | }); |
| 114 | ``` |
| 115 | |
| 116 | Replace both IDs with your own. Referenced projects and branches are not owned by |
| 117 | the Website. When declaring a Project for a combined website/storage backend, |
| 118 | choose `region: "aws-us-east-2"` (Ohio) and verify service availability for your |
| 119 | account. |
| 120 | |
| 121 | The deployment account key is not an application credential. |
| 122 | [`Neon.Credential`](/providers/neon/credential) grants branch-and-descendant |
| 123 | permissions, not per-bucket permissions. Request only needed scopes; storage |
| 124 | reads currently require explicit `storage:read`, even with `storage:write`. |
| 125 | Never put account keys or privileged service tokens in a browser bundle. |
| 126 | |
| 127 | ## Environment and deployment limits |
| 128 | |
| 129 | The website's `env` is supplied to build, native dev, and Function configuration. |
| 130 | Vite embeds `VITE_*` values into browser assets; there is no Vite server application |
| 131 | to read private runtime values. `Redacted` does not prevent a build tool from |
| 132 | embedding secrets. SSR constructors use the same shared environment contract, |
| 133 | with their own framework's public-variable rules. |
| 134 | |
| 135 | :::caution[Verification boundaries] |
| 136 | Native development and local Fetch tests do not establish live acceptance for |
| 137 | all frameworks. Live probes have observed new active deployment IDs while the |
| 138 | Function URL still served old code and environment. Verify actual HTTP behavior |
| 139 | after a deploy; metadata success or `memo: false` is not a verified workaround. |
| 140 | ::: |
| 141 | |
| 142 | Static files are served inside the Function, currently with |
| 143 | `Cache-Control: no-cache`; this composition adds no CDN or durable framework cache. |
| 144 | The `domain` prop registers a custom hostname and exposes `site.domain.cnameTarget` for a |
| 145 | DNS-only CNAME. Registration and the returned URL do not establish DNS propagation |
| 146 | or valid TLS; verify HTTPS separately. Follow [Custom domains](/neon/guides/custom-domains) |
| 147 | for DNS, CAA, HTTPS checks, and safe retirement, or see the |
| 148 | [`CustomDomain` reference](/providers/neon/customdomain). |
| 149 | |
| 150 | ## Reference |
| 151 | |
| 152 | - [Vite API](/providers/neon/website/vite) |
| 153 | - [Function API](/providers/neon/function) |
| 154 | - [Runnable Vite example](https://github.com/alchemy-run/alchemy/tree/main/examples/neon-website-vite) |