SSR specifically refers to front-end frameworks (for example React, Preact, Vue, and Svelte) that support running the same application in Node.js, pre-rendering it to HTML, and finally hydrating it on the client. If you are looking for integration with traditional server-side frameworks, check out the Backend Integration guide instead.The following guide also assumes prior experience working with SSR in your framework of choice, and will only focus on Vite-specific integration details.
Example Projects
Vite provides built-in support for server-side rendering (SSR).create-vite-extra contains example SSR setups you can use as references for this guide:
Source Structure
A typical SSR application will have the following source file structure:index.html will need to reference entry-client.js and include a placeholder where the server-rendered markup should be injected:
index.html
You can use any placeholder you prefer instead of
<!--ssr-outlet-->, as long as it can be precisely replaced.Conditional Logic
If you need to perform conditional logic based on SSR vs. client, you can use:Setting Up the Dev Server
When building an SSR app, you likely want to have full control over your main server and decouple Vite from the production environment. It is therefore recommended to use Vite in middleware mode. Here is an example with express:Create Vite server in middleware mode
server.js
vite is an instance of ViteDevServer. vite.middlewares is a Connect instance which can be used as a middleware in any connect-compatible Node.js framework.Implement server-rendered HTML handler
The next step is implementing the
* handler to serve server-rendered HTML:server.js
Building for Production
To ship an SSR project for production, we need to:- Produce a client build as normal
- Produce an SSR build, which can be directly loaded via
import()so that we don’t have to go through Vite’sssrLoadModule
package.json will look like this:
package.json
The
--ssr flag indicates this is an SSR build. It should also specify the SSR entry.server.js we need to add some production specific logic by checking process.env.NODE_ENV:
- Instead of reading the root
index.html, use thedist/client/index.htmlas the template, since it contains the correct asset links to the client build - Instead of
await vite.ssrLoadModule('/src/entry-server.js'), useimport('./dist/server/entry-server.js')(this file is the result of the SSR build) - Move the creation and all usage of the
vitedev server behind dev-only conditional branches, then add static file serving middlewares to serve files fromdist/client
Generating Preload Directives
vite build supports the --ssrManifest flag which will generate .vite/ssr-manifest.json in build output directory:
package.json
dist/client/.vite/ssr-manifest.json for the client build (Yes, the SSR manifest is generated from the client build because we want to map module IDs to client files). The manifest contains mappings of module IDs to their associated chunks and asset files.
To leverage the manifest, frameworks need to provide a way to collect the module IDs of the components that were used during a server render call.
@vitejs/plugin-vue supports this out of the box and automatically registers used component module IDs on to the associated Vue SSR context:
src/entry-server.js
In the production branch of
server.js we need to read and pass the manifest to the render function exported by src/entry-server.js. This would provide us with enough information to render preload directives for files used by async routes! See demo source for a full example. You can also use this information for 103 Early Hints.Pre-Rendering / SSG
If the routes and the data needed for certain routes are known ahead of time, we can pre-render these routes into static HTML using the same logic as production SSR. This can also be considered a form of Static-Site Generation (SSG). See demo pre-render script for working example.SSR Externals
Dependencies are “externalized” from Vite’s SSR transform module system by default when running SSR. This speeds up both dev and build. If a dependency needs to be transformed by Vite’s pipeline, for example, because Vite features are used untranspiled in them, they can be added tossr.noExternal.
For linked dependencies, they are not externalized by default to take advantage of Vite’s HMR. If this isn’t desired, for example, to test dependencies as if they aren’t linked, you can add it to ssr.external.
SSR-specific Plugin Logic
Some frameworks such as Vue or Svelte compile components into different formats based on client vs. SSR. To support conditional transforms, Vite passes an additionalssr property in the options object of the following plugin hooks:
resolveIdloadtransform
load and transform is optional, Rollup is not currently using this object but may extend these hooks with additional metadata in the future.
Before Vite 2.7, this was informed to plugin hooks with a positional
ssr param instead of using the options object. All major frameworks and plugins are updated but you may find outdated posts using the previous API.SSR Target
The default target for the SSR build is a node environment, but you can also run the server in a Web Worker. Packages entry resolution is different for each platform. You can configure the target to be Web Worker using thessr.target set to 'webworker'.
SSR Bundle
In some cases likewebworker runtimes, you might want to bundle your SSR build into a single JavaScript file. You can enable this behavior by setting ssr.noExternal to true. This will do two things:
- Treat all dependencies as
noExternal - Throw an error if any Node.js built-ins are imported
SSR Resolve Conditions
By default package entry resolution will use the conditions set inresolve.conditions for the SSR build. You can use ssr.resolve.conditions and ssr.resolve.externalConditions to customize this behavior.
Vite CLI
The CLI commands$ vite dev and $ vite preview can also be used for SSR apps. You can add your SSR middlewares to the development server with configureServer and to the preview server with configurePreviewServer.