Vite
Railway.Website.Vite deploys a Vite project to
Railway. It runs vite build and serves the output from a generated
Node static-file server on one
Railway.Service. Omit project and Alchemy creates a
Railway.Project. Live url is
https://{name}.up.railway.app. Your vite.config.* loads natively
— plugins included. There is no framework server module.
React, Vue, and Solid client SPAs belong here, as do multi-page apps
that emit one HTML file per route. SSR frameworks that wrap Vite
deploy through their own composites —
Astro,
Nextjs,
Nuxt,
ReactRouter,
SolidStart,
SvelteKit,
TanStackStart,
Waku,
Octane.
Install
Section titled “Install”The build integration is not bundled with alchemy. Install
@alchemy.run/frontend-frameworks; the resource loads its /vite
and /vite/node exports from your project at deploy time. It is
only used at build time, so a dev dependency is enough:
bun add -d @alchemy.run/frontend-frameworksnpm install -D @alchemy.run/frontend-frameworkspnpm add -D @alchemy.run/frontend-frameworksyarn add -D @alchemy.run/frontend-frameworksConfigure Vite
Section titled “Configure Vite”Your vite.config.* file loads natively — React, Vue, or Solid
plugins, Tailwind, and any other non-serializable options work
exactly as they do outside Alchemy. Alchemy merges a programmatic
config over it via the vite prop — see
Vite configuration below.
import { defineConfig } from "vite";import react from "@vitejs/plugin-react";import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ plugins: [react(), tailwindcss()],});Do not set a Vite SSR adapter. This composite never creates a framework server module — it only ships the client build.
Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack):
import * as Railway from "alchemy/Railway";
export const Website = Railway.Website.Vite("Website");Pass project to put the Service in a Project you already have.
Omit it and Alchemy creates Railway.Project("Project") under the
site’s namespace:
export const Site = Railway.Project("Site");
export const Website = Railway.Website.Vite("Website", { project: Site,});Add it to the Stack
Section titled “Add it to the Stack”Yield the site from your Stack and return its URL:
import * as Alchemy from "alchemy";import * as Effect from "effect/Effect";
export default Alchemy.Stack( "MyViteSite", { providers: Railway.providers(), state: Alchemy.localState(), }, Effect.gen(function* () { const site = yield* Website; return { url: site.url }; }),);On deploy, url is the generated *.up.railway.app hostname.
service and project are the backing resources; they are
undefined under alchemy dev.
See examples/railway-website-vite for the checked-in example.
Add environment variables
Section titled “Add environment variables”env is process environment — set on process.env before
vite build / alchemy dev, and on the Service at deploy. It is
not Worker bindings:
export const Website = Railway.Website.Vite("Website", { env: { VITE_GREETING: "Hello from Alchemy!", },});Vite inlines VITE_* into the client bundle at build time. Other
keys reach the hosted Node process but not the browser.
Read the environment
Section titled “Read the environment”Client code reads inlined values from import.meta.env:
const greeting = import.meta.env.VITE_GREETING ?? "hello";Single-page vs multi-page
Section titled “Single-page vs multi-page”Plain Vite apps are typically SPAs, so assets.notFoundHandling
defaults to "single-page-application": unmatched paths get
index.html with status 200 so client-side routes deep-link. Use
"404-page" for multi-page sites that ship a 404.html:
export const Website = Railway.Website.Vite("Docs", { assets: { notFoundHandling: "404-page" },});Vite configuration
Section titled “Vite configuration”rootDir is the project root (the directory containing
package.json). Default ".".
vite.outDir and vite.base are serializable overrides merged
over the config file:
export const Website = Railway.Website.Vite("Website", { rootDir: "./app", vite: { outDir: "build", base: "/docs/" },});outDir is relative to rootDir and defaults to the file’s
build.outDir (Vite’s default: "dist"). base is Vite’s public
base path.
Local development
Section titled “Local development”alchemy dev runs Vite’s own dev server (native HMR) instead of
deploying; site.url is the local address and no Project or Service
is created. Wrap the site in
Alchemy.remote() to deploy the real infrastructure even during
dev.
Custom domain
Section titled “Custom domain”domain is a hostname string. Alchemy attaches a
Railway.CustomDomain (targetPort 3000)
and url becomes https://{domain}:
export const Website = Railway.Website.Vite("Website", { domain: "app.example.com",});Where next
Section titled “Where next”Railway.Website.ViteAPI- Astro, Next.js, Nuxt, SvelteKit, Waku, Octane — SSR frameworks
- Projects, Services, Custom domains