SolidStart
AWS.Website.SolidStart deploys a
SolidStart app to AWS. It runs your
project’s own vite build and appends nitro’s aws-lambda preset:
the SSR server runs on a Lambda Function URL with response streaming,
and client assets plus prerendered pages are served from a private S3
bucket through CloudFront. A CloudFront Function routes each request
at the edge — uploaded files go to S3, everything else streams from
the Lambda.
Install
Section titled “Install”The build integration is not bundled with alchemy. Install
@alchemy.run/frontend-frameworks; the resource loads its
/solidstart and /solidstart/aws 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-frameworks@solidjs/vite-plugin-nitro-2 is the server half of a SolidStart
build and must be a project dependency too:
bun add @solidjs/vite-plugin-nitro-2npm install @solidjs/vite-plugin-nitro-2pnpm add @solidjs/vite-plugin-nitro-2yarn add @solidjs/vite-plugin-nitro-2Configure Vite
Section titled “Configure Vite”Your vite.config.ts is just the SolidStart plugin — no adapter, no
nitro wiring:
import { solidStart } from "@solidjs/start/config";import { defineConfig } from "vite";
export default defineConfig({ plugins: [solidStart()],});Alchemy appends its own nitroV2Plugin() instance at build time,
carrying the aws-lambda preset with response streaming enabled.
Don’t register nitroV2Plugin() yourself — the appended instance
would discard its options, so the build fails with an actionable
error instead. Pass nitro options through the resource’s nitro
prop; see Prerendering below.
Declare the Website
Section titled “Declare the Website”Declare the site as a module-level const (rather than inline in the Stack):
import * as AWS from "alchemy/AWS";
export const Website = AWS.Website.SolidStart("Website");rootDir is the project root — the directory holding package.json
and vite.config.*. It defaults to ".", so set it only when the app
does not sit next to alchemy.run.ts:
export const Website = AWS.Website.SolidStart("Website", { rootDir: "./app",});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( "MySolidStartSite", { providers: AWS.providers(), state: AWS.state(), }, Effect.gen(function* () { const site = yield* Website; return { url: site.url }; }),);Add environment variables
Section titled “Add environment variables”The server function’s environment is configured under env:
export const Website = AWS.Website.SolidStart("Website", { memorySize: 2048, env: { API_BASE: "https://api.example.com", },});env values are set on the Lambda on deploy and injected into
the dev server’s process environment under alchemy dev, so server
code reads the same values in both modes. Sibling props tune the
Lambda itself (memorySize, timeout, architecture, runtime).
An output from another resource only exists once that resource is yielded, so declare the site inside the Stack generator instead:
Effect.gen(function* () { const api = yield* Api;
const site = yield* AWS.Website.SolidStart("Website", { env: { API_BASE: api.url }, });
return { url: site.url };});Read the environment in server code
Section titled “Read the environment in server code”The SolidStart server runs in a plain Node Lambda, so API routes,
server functions, and SSR read the environment from process.env:
export function GET() { return new Response(process.env.API_BASE ?? "unset");}Values prefixed VITE_ are inlined into the client bundle at build
time instead, as import.meta.env.VITE_* — set those in the shell
that runs alchemy deploy, not in env.
Prerendering
Section titled “Prerendering”Nitro renders the routes you list at build time into .output/public.
They are uploaded to S3 and served from the edge with no Lambda
invocation:
export const Website = AWS.Website.SolidStart("Website", { nitro: { prerender: { routes: ["/", "/about"] } },});prerender takes the routes to render (routes) and can crawl links
from prerendered pages to prerender them too (crawlLinks). The
nitro prop is the home for all nitro options (route rules, storage,
…) — the integration owns the plugin instance, so they cannot live
in vite.config.ts. Values must be JSON-serializable; preset is
owned by the AWS deploy target, and a foreign preset is a hard error.
Local dev
Section titled “Local dev”bun alchemy devalchemy dev runs SolidStart’s own Vite dev server (native HMR)
instead of deploying anything; site.url is the dev server’s
http://localhost:<port> address and no AWS resources are created.
Nitro plays no part in dev. Wrap the site in Alchemy.remote() to
deploy the real infrastructure even during dev.
SolidStart’s plugin resolves its app root and route directory from
the process working directory, so one alchemy dev process serves
one SolidStart app at a time.
Custom domain
Section titled “Custom domain”export const Website = AWS.Website.SolidStart("Website", { domain: { name: "app.example.com" },});The certificate is created in us-east-1 (required by CloudFront),
DNS validation records and the alias record are managed through
Route 53. The hosted zone is inferred from the hostname — pass
hostedZoneId to pin it when several zones could match.
Share a distribution with Router
Section titled “Share a distribution with Router”CloudFront distributions take minutes to create. To serve several
sites (or a site plus an API) from one distribution, attach the site
to an AWS.Website.Router:
The site takes the Router’s outputs, so both are yielded inside the Stack generator:
Effect.gen(function* () { const router = yield* AWS.Website.Router("FrontDoor", { domain: { name: "example.com" }, });
const site = yield* AWS.Website.SolidStart("Website", { domain: { router }, });
return { url: site.url };});