diff --git a/src/routes/(3)building-apps/(0)app-structure.mdx b/src/routes/(3)building-apps/(0)app-structure.mdx
index b9ee3ba94..ae644812f 100644
--- a/src/routes/(3)building-apps/(0)app-structure.mdx
+++ b/src/routes/(3)building-apps/(0)app-structure.mdx
@@ -12,7 +12,7 @@ Those two components are the whole application surface.
The plugin's start mode generates the rest: the server entry that renders ``, the client entry that mounts or hydrates it, and the request handler that serves both.
Most apps need only `App.tsx` and `Document.tsx`, and the first two sections below are about those files.
-The sections on generated entries and `start.setup` are for projects that need to change the defaults, such as an authored server entry or a router that must load before the render starts.
+The sections on generated entries and `start.serverRenderSetup` are for projects that need to change the defaults, such as an authored server entry or a router that must load before the render starts.
## Three rendering modes, one layout
@@ -159,13 +159,13 @@ Start mode does not select a router.
Mount the router or its provider inside `App`, the way both router variants of the templates do, and route modules and route-table generation stay with the router you chose.
Some routers need an instance bound to the current request so they can load the matched routes before the server render begins.
-`start.setup` names a server-only module for that:
+`start.serverRenderSetup` names a server-only module for that:
```ts
// vite.config.ts
solid({
start: {
- setup: "./src/setup.tsx",
+ serverRenderSetup: "./src/setup.tsx",
},
ssr: true,
});
@@ -176,7 +176,7 @@ The generated server entry calls it after the middleware chain has dispatched to
It may return a component, nothing, or a promise of either: a returned component renders in `App`'s place inside `Document`, and no return keeps ``.
The browser-side `App` must produce the same router tree, or hydration will not match.
-`start.setup` runs only for page renders made by a generated server entry under `ssr: true`.
+`start.serverRenderSetup` runs only for page renders made by a generated server entry under `ssr: true`.
It is ignored without `ssr`, and combining it with an authored server entry is an error.
[Integrate a router](/routing/integrate-a-router) shows how the two supported routers use this hook and when a project needs it at all.
@@ -204,22 +204,22 @@ Production builds wrap the generated entries in a default error boundary that lo
Read the server log for the real error.
Set `start.productionErrorBoundary: false` when middleware owns error handling; [Middleware and API routes](/building-apps/middleware-and-api-routes#catching-errors) shows that middleware.
-### `start.setup only applies to generated entries`
+### `start.serverRenderSetup only applies to generated entries`
-The project has an authored `src/entry-server.*` (or `start.entries.server`) and `start.setup` at the same time.
-Call the setup step from your own `render()` and remove `start.setup`, or remove the authored entry.
+The project has an authored `src/entry-server.*` (or `start.entries.server`) and `start.serverRenderSetup` at the same time.
+Call the setup step from your own `render()` and remove `start.serverRenderSetup`, or remove the authored entry.
## Loading instrumentation first
An error monitor or a tracing SDK on the server patches `node:http` and the other modules it observes, so it has to run before those modules are imported.
Writing `import "./instrument"` at the top of an entry does not achieve that in ESM: static imports are hoisted and evaluated in dependency order, so the entry's own dependencies load first.
-`start.instrument` names a server-only module the plugin awaits before anything else in the server graph:
+`start.serverSetup` names a server-only module the plugin awaits before anything else in the server graph:
```ts
// vite.config.ts
solid({
start: {
- instrument: "./src/instrument.ts",
+ serverSetup: "./src/instrument.ts",
},
ssr: true,
});
@@ -245,8 +245,8 @@ Keep code splitting on in the server build (the default); inlining dynamic impor
- Render `{props.children}` in the document body, or the app never appears on the server.
- The static `
` in `Document` is the fallback; per-page titles come from head metadata.
- Author `entry-server` and `entry-client` together, and only when the generated pair cannot produce the response you need.
-- Use `start.setup` when a router must load against the request before the server render; it applies only to generated entries under `ssr: true`.
-- Use `start.instrument` for a module that must run before the server graph loads, such as a monitoring SDK's `init()`; a static import at the top of an entry does not run first.
+- Use `start.serverRenderSetup` when a router must load against the request before the server render; it applies only to generated entries under `ssr: true`.
+- Use `start.serverSetup` for a module that must run before the server graph loads, such as a monitoring SDK's `init()`; a static import at the top of an entry does not run first.
## Next steps
diff --git a/src/routes/(3)building-apps/(7)deployment.mdx b/src/routes/(3)building-apps/(7)deployment.mdx
index 68a80fc96..4afd2f5bd 100644
--- a/src/routes/(3)building-apps/(7)deployment.mdx
+++ b/src/routes/(3)building-apps/(7)deployment.mdx
@@ -385,7 +385,10 @@ function isFromOldBuild(err: unknown) {
```
A function whose id survives a deploy can still take other arguments or return another shape.
-To catch that too, give the server a [`buildId`](/reference/solid-web/server-functions/host-configuration#buildid) per deploy, such as the commit hash, and send the same id from the client in the `X-Server-Function-Build` header through `prepareRequest`.
+To catch that too, the server checks a [`buildId`](/reference/solid-web/server-functions/host-configuration#buildid) against the `X-Server-Function-Build` header the client sends.
+`@solidjs/vite-plugin` wires both sides in production builds: the client build stamps every non-GET call with a fresh id per build, and the server build is configured with the same id, so a new deploy refuses calls from tabs still running the previous one.
+An app's own `prepareRequest` hook still runs, after the plugin's.
+Without the plugin, give the server a `buildId` per deploy, such as the commit hash, and send the same id from the client in that header through `prepareRequest`.
A call from another build is answered `409` without running the function, and rejects with the same `unknownFunction: true`.
## Common problems
diff --git a/src/routes/(4)routing/(2)tanstack/(0)index.mdx b/src/routes/(4)routing/(2)tanstack/(0)index.mdx
index 8164d2649..86825589d 100644
--- a/src/routes/(4)routing/(2)tanstack/(0)index.mdx
+++ b/src/routes/(4)routing/(2)tanstack/(0)index.mdx
@@ -168,7 +168,7 @@ For forms that must work without JavaScript, the `fullstack` shape's Solid Route
## What the server does per request
-The plugin's `start.setup` hook is the seam that lets a router Solid does not own drive streaming SSR.
+The plugin's `start.serverRenderSetup` hook is the seam that lets a router Solid does not own drive streaming SSR.
`src/setup.tsx` runs for each SSR request:
```tsx
@@ -278,7 +278,7 @@ The one adjustment is where API routes live: `src/routes` belongs to TanStack Ro
## Limits to know
-- `start.setup` runs only with the plugin's generated server entry and only when `ssr` is on.
+- `start.serverRenderSetup` runs only with the plugin's generated server entry and only when `ssr` is on.
An authored server entry has to prepare the router and render it itself.
- TanStack's own SSR protocol (`RouterServer`, `RouterClient`, and the `$_TSR` stream handlers) expects to own the HTML stream, and under start mode the plugin owns it.
The template does not use that protocol; the hydration-registry transfer above is the native channel for this setup.
@@ -293,12 +293,12 @@ The one adjustment is where API routes live: `src/routes` belongs to TanStack Ro
- Read `query.data` directly and use `isPending` for refetch state; skip the `isLoading` guards.
- Call mutations through `useMutation` with an arrow around the server function, and return `reload({ revalidate })` to scope what single-flight collects.
- Keep `staleTime` above zero so hydrated and single-flight data count as fresh.
-- Build a fresh router and `QueryClient` per request in `start.setup`, and copy a redirect or status from `router._serverResult` onto the response.
+- Build a fresh router and `QueryClient` per request in `start.serverRenderSetup`, and copy a redirect or status from `router._serverResult` onto the response.
- Leave `disableGlobalCatchBoundary: true` in place so errors and redirects reach the app's own boundaries during SSR.
## Next steps
- [Server functions](/building-apps/server-functions): the reads and mutations wrapped in `queryOptions` and `useMutation`, including why `GET` matters.
-- [Integrate a router](/routing/integrate-a-router): the `start.setup` and flight-data hooks this template is built on, for adapting the pattern to another router.
+- [Integrate a router](/routing/integrate-a-router): the `start.serverRenderSetup` and flight-data hooks this template is built on, for adapting the pattern to another router.
- [Choose a rendering mode](/guides/choose-a-rendering-mode): the `ssr: false` posture, a static shell plus API server, works for this shape too.
- [Middleware and API routes](/building-apps/middleware-and-api-routes): the `src/api` handlers, and why they live outside `src/routes` in this shape.
diff --git a/src/routes/(4)routing/(3)integrate-a-router.mdx b/src/routes/(4)routing/(3)integrate-a-router.mdx
index 0a1c7f4df..30ab090fe 100644
--- a/src/routes/(4)routing/(3)integrate-a-router.mdx
+++ b/src/routes/(4)routing/(3)integrate-a-router.mdx
@@ -20,13 +20,13 @@ A router that does the same needs nothing else from the platform for a synchrono
### Prepare an instance per request
-A router that must do asynchronous work before rendering, such as creating a request-bound instance and awaiting its route loaders, uses the `start.setup` option:
+A router that must do asynchronous work before rendering, such as creating a request-bound instance and awaiting its route loaders, uses the `start.serverRenderSetup` option:
```ts
// vite.config.ts
solid({
start: {
- setup: "./src/setup.tsx",
+ serverRenderSetup: "./src/setup.tsx",
},
ssr: true,
});
@@ -52,14 +52,14 @@ The browser-side `App` must produce the matching router tree so hydration lines
The [TanStack Router](/routing/tanstack#what-the-server-does-per-request) page shows a complete `setup.tsx`, including how it copies a redirect or a `404` from the router onto the response.
:::caution[Where the hook runs and where it does not]
-`start.setup` runs only when a generated server entry renders an SSR request.
+`start.serverRenderSetup` runs only when a generated server entry renders an SSR request.
It is ignored when `ssr` is off, and configuring it together with an authored `entry-server` is a build error, because an authored entry already owns its render function and must perform the same preparation itself.
:::
### Middleware shares the event
The separate `start.middleware` option installs a fetch-style chain in front of page renders, server-function calls, API routes, and no-JavaScript form posts.
-The chain and `start.setup` receive the same request event, so a value a middleware stores in `event.locals`, such as a session, is visible to the router instance the setup hook builds.
+The chain and `start.serverRenderSetup` receive the same request event, so a value a middleware stores in `event.locals`, such as a session, is visible to the router instance the setup hook builds.
[Middleware and API routes](/building-apps/middleware-and-api-routes) covers the chain itself.
## Integrate single-flight mutations
@@ -119,14 +119,14 @@ Its `QueryClientProvider` subscribes as `"sq"` while mounted, the server hook bu
## Common problems
-### The build fails with `start.setup only applies to generated entries`
+### The build fails with `start.serverRenderSetup only applies to generated entries`
-The project has an authored `entry-server` and a `start.setup` option at the same time.
-Remove one: either delete the authored entry so the generated one runs the hook, or move the preparation into the authored entry's own render function and drop `start.setup`.
+The project has an authored `entry-server` and a `start.serverRenderSetup` option at the same time.
+Remove one: either delete the authored entry so the generated one runs the hook, or move the preparation into the authored entry's own render function and drop `start.serverRenderSetup`.
### The setup module never runs
-`start.setup` is server-mode only.
+`start.serverRenderSetup` is server-mode only.
With `ssr: false` there is no per-request app render to prepare, so the option is accepted and ignored.
Check the `ssr` flag in `vite.config.ts` before looking at the module.
@@ -143,8 +143,8 @@ The unnamed server hook pairs with the one-argument `subscribeFlightData(consume
## Recap
- Read the request URL from the request event during SSR; the handler runs the render inside that event's scope.
-- Use `start.setup` for a router that needs a per-request instance or asynchronous loading before render, and return the component to render in `App`'s place.
-- `start.setup` applies only to generated server entries with `ssr` on; an authored entry does the same work itself.
+- Use `start.serverRenderSetup` for a router that needs a per-request instance or asynchronous loading before render, and return the component to render in `App`'s place.
+- `start.serverRenderSetup` applies only to generated server entries with `ssr` on; an authored entry does the same work itself.
- Values a middleware stores in `event.locals` are visible in the setup hook, because both receive the same request event.
- Opt in to single-flight on the client with `subscribeFlightData`; the header is sent on non-`GET` calls while a consumer is registered.
- Register the server-side `collectFlightData` hook from the `serverFunctions.configure` module so it loads before the first dispatch.
diff --git a/src/routes/(5)guides/(15)observability.mdx b/src/routes/(5)guides/(15)observability.mdx
index 702da0f1a..e13ce5bfc 100644
--- a/src/routes/(5)guides/(15)observability.mdx
+++ b/src/routes/(5)guides/(15)observability.mdx
@@ -114,7 +114,7 @@ The context then carries the request's `event`, and the hook's return value is i
The hook runs inside the request scope, so `getRequestEvent()` works in it.
`renderToStream(App, { onError })` and `renderToString(App, { onError })` take a per-request hook that wins over the ambient one for that request.
-A monitoring SDK's `init()` has to run before the modules it patches load; the plugin's [`start.instrument`](/building-apps/app-structure#loading-instrumentation-first) option is where a plugin-hosted app puts it, and the module above is the shape it takes.
+A monitoring SDK's `init()` has to run before the modules it patches load; the plugin's [`start.serverSetup`](/building-apps/app-structure#loading-instrumentation-first) option is where a plugin-hosted app puts it, and the module above is the shape it takes.
:::danger[The return value goes on the wire]
The hook receives the error as thrown.
diff --git a/src/routes/(6)migration/(2)from-solid-start.mdx b/src/routes/(6)migration/(2)from-solid-start.mdx
index d8b3ee08d..9f3e3eb48 100644
--- a/src/routes/(6)migration/(2)from-solid-start.mdx
+++ b/src/routes/(6)migration/(2)from-solid-start.mdx
@@ -177,7 +177,7 @@ export default defineConfig({
```
`start: true` and `start: {}` enable the same mode.
-Use the object form only when the application needs `app`, `entries`, `document`, `middleware`, `setup`, or `externalServer`; typed environment variables use the top-level `env` option.
+Use the object form only when the application needs `app`, `entries`, `document`, `middleware`, `serverSetup`, `serverRenderSetup`, or `externalServer`; typed environment variables use the top-level `env` option.
The `extensions` entry makes the Solid transform accept route module IDs generated with query suffixes by `filesystem-routing`.
Map old configuration deliberately:
@@ -206,7 +206,7 @@ Generated SSR entries:
The start-mode handler creates the request event used by middleware, page rendering, and server functions.
-Keep generated entries unless the old entries contain behavior that cannot move into `App`, `Document`, middleware, `start.setup`, or a server function.
+Keep generated entries unless the old entries contain behavior that cannot move into `App`, `Document`, middleware, `start.serverRenderSetup`, or a server function.
When authored entries are necessary, rewrite both files as a pair.
The server entry must export `render(request?, context?)`, and the client entry must hydrate the same document tree.
@@ -284,7 +284,7 @@ See the [file-system route conventions](/reference/filesystem-routing/convention
For TanStack Router, start from the pinned `fullstack-tanstack` template.
Its router plugin owns `src/routes` and generates `routeTree.gen.ts`.
-For SSR, `start.setup` creates and loads a request-specific router before the generated entry renders.
+For SSR, `start.serverRenderSetup` creates and loads a request-specific router before the generated entry renders.
Do not reuse SolidStart's router server or client entry protocol.
See the [TanStack Router integration](/routing/tanstack).
diff --git a/src/routes/reference/(5)vite-plugin-solid/(2)start.mdx b/src/routes/reference/(5)vite-plugin-solid/(2)start.mdx
index b972ef622..cd9b7132b 100644
--- a/src/routes/reference/(5)vite-plugin-solid/(2)start.mdx
+++ b/src/routes/reference/(5)vite-plugin-solid/(2)start.mdx
@@ -104,15 +104,25 @@ type Middleware = (
Middleware runs in array order inside the request-event scope.
The middleware fronts every request dispatched by the generated handler, including pages and the server-function endpoint.
-### `setup`
+### `serverSetup`
- **Type:** `string`
- **Default:** `undefined`
-Specifies a server-only module whose default export prepares the application before generated SSR begins.
+Specifies a server-only module the plugin runs to completion once, before anything else in the server graph loads: the app, the middleware, `@solidjs/web`, and every dependency.
+It is the place for a monitoring SDK's `init()`, which has to run before the modules it patches load.
+The module needs no exports and may use top-level `await`.
+It is the server counterpart of `clientSetup`.
+
+### `serverRenderSetup`
+
+- **Type:** `string`
+- **Default:** `undefined`
+
+Specifies a server-only module whose default export runs before every generated server render.
```ts
-type Setup = (
+type ServerRenderSetup = (
event: RequestEvent,
App: Component
) => Component | void | Promise;
@@ -120,8 +130,8 @@ type Setup = (
Return a component to replace `App` for that request.
Return nothing to keep `App`.
-The plugin ignores `setup` in client mode and rejects it when SSR uses authored entries.
-The setup function does not run for server-function endpoint dispatch.
+The plugin ignores `serverRenderSetup` in client mode and rejects it when SSR uses authored entries.
+The function does not run for server-function endpoint dispatch.
### `productionErrorBoundary`