diff --git a/docs/config/index.md b/docs/config/index.md index 49983a551c7453..472a103bb61e95 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -462,6 +462,13 @@ export default defineConfig(({ command, mode }) => { `envPrefix` should not be set as `''`, which will expose all your env variables and cause unexpected leaking of of sensitive information. Vite will throw error when detecting `''`. ::: +### spa + +- **Type:** `boolean` +- **Default:** `true` + + Whether your application is a Single Page Application (SPA). Set to `false` for other kinds of apps like MPAs. Learn more in Vite's [SSR guide](/guide/ssr#vite-cli). + ## Server Options ### server.host diff --git a/docs/guide/ssr.md b/docs/guide/ssr.md index ee078e82eb60a1..4e75453bbac86a 100644 --- a/docs/guide/ssr.md +++ b/docs/guide/ssr.md @@ -264,3 +264,14 @@ In some cases like `webworker` runtimes, you might want to bundle your SSR build - Treat all dependencies as `noExternal` - Throw an error if any Node.js built-ins are imported + +## Vite CLI + +The CLI commands `$ vite dev` and `$ vite preview` can also be used for SSR apps: + +1. Add your SSR middleware to the development server with [`configureServer`](/guide/api-plugin#configureserver) and to the preview server with [`configurePreviewServer`](/guide/api-plugin#configurepreviewserver). + :::tip Note + Use a post hook so that your SSR middleware runs _after_ Vite's middlewares. + ::: + +2. Set `config.spa` to `false`. This switches the development and preview server from SPA mode to SSR/MPA mode. diff --git a/packages/vite/src/node/config.ts b/packages/vite/src/node/config.ts index 174fa0dd40f3d0..4a9099b1982d62 100644 --- a/packages/vite/src/node/config.ts +++ b/packages/vite/src/node/config.ts @@ -208,6 +208,12 @@ export interface UserConfig { 'plugins' | 'input' | 'onwarn' | 'preserveEntrySignatures' > } + /** + * Whether your application is a Single Page Application (SPA). Set to `false` + * for other kinds of apps like MPAs. + * @default true + */ + spa?: boolean } export interface ExperimentalOptions { @@ -274,6 +280,7 @@ export type ResolvedConfig = Readonly< /** @internal */ packageCache: PackageCache worker: ResolveWorkerOptions + spa: boolean } > @@ -514,7 +521,8 @@ export async function resolveConfig( ...optimizeDeps.esbuildOptions } }, - worker: resolvedWorkerOptions + worker: resolvedWorkerOptions, + spa: config.spa ?? true } // flat config.worker.plugin diff --git a/packages/vite/src/node/preview.ts b/packages/vite/src/node/preview.ts index e66733d5c971b6..a8cb533e35ad19 100644 --- a/packages/vite/src/node/preview.ts +++ b/packages/vite/src/node/preview.ts @@ -104,7 +104,7 @@ export async function preview( sirv(distDir, { etag: true, dev: true, - single: true + single: config.spa }) ) diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts index e59de9f2e50690..a8e16b2b5cf9e5 100644 --- a/packages/vite/src/node/server/index.ts +++ b/packages/vite/src/node/server/index.ts @@ -508,8 +508,10 @@ export async function createServer( middlewares.use(serveRawFsMiddleware(server)) middlewares.use(serveStaticMiddleware(root, server)) + const isMiddlewareMode = middlewareMode && middlewareMode !== 'html' + // spa fallback - if (!middlewareMode || middlewareMode === 'html') { + if (config.spa && !isMiddlewareMode) { middlewares.use(spaFallbackMiddleware(root)) } @@ -518,9 +520,12 @@ export async function createServer( // serve custom content instead of index.html. postHooks.forEach((fn) => fn && fn()) - if (!middlewareMode || middlewareMode === 'html') { + if (config.spa && !isMiddlewareMode) { // transform index.html middlewares.use(indexHtmlMiddleware(server)) + } + + if (!isMiddlewareMode) { // handle 404s // Keep the named function. The name is visible in debug logs via `DEBUG=connect:dispatcher ...` middlewares.use(function vite404Middleware(_, res) {