A Vite plugin that improves the DX of developing SSR apps.
It is:
- ✅ Framework agnostic on the client (use react, solid, etc)
- ✅ Framework agnostic on the server (use node 18+, hono, h3, cloudflare, bun, deno, etc)
- ✅ Simple "native" Vite - continue using
vite dev
,vite build
, etc
It enables:
- Route based code-spliting with asset pre-loading
- Typescript + HMR support on the client AND server
- Elimates FOUC css issues during development
- Generates a
ssr-manifest.json
file during build that maps client route urls -> assets - Provides a
assetsForRequest(url: string)
function on the server that returns a list of assets critical to the given request (along with preload links, etc). You can use this to inject the appropriate asset tags.
❗ A small disclaimer... SSRx intentionally does not try to do everything and is intended for a specific audience. If you're looking for a full-fledged framework, SSRx might not be for you. If you are looking to build a modern SSR app with your choice of 3rd party libraries for routing, head management, etc, then SSRx might be right for you.
❗ Remix is transitioning to Vite, so for Vite + React Router projects I now recommend Remix as the best-in-class option.
The SSRx Vite plugin is barebones and (mostly) unopinionated by design, and can be used standalone. See the
bun-react-router
, react-router-simple
,
tanstack-router-simple
, and solid-router-simple
examples.
@ssrx/vite
is mostly unopinionated, but does require 3 things (the file locations are configurable, defaults below):
Install deps via yarn, npm, etc
yarn add @ssrx/vite
yarn add -D vite@5
Requirement #1 - a client entry file, `src/entry.client.tsx`
This file should mount your application in the browser. For React it might look something like this:
// src/entry.client.tsx
import { hydrateRoot } from 'react-dom/client';
import { App } from '~/app.tsx';
hydrateRoot(document, <App />);
Requirement #2 - a server file, `src/server.ts`
A server entry who's default export includes a fetch
function that accepts a
Request and returns a
Response object with your rendered or streamed app.
@ssrx/vite
is focused on supporting the WinterCG standard. Modern node frameworks such asHono
andh3
, as well as alternative runtimes such asbun
,deno
,cloudflare
, and more should all work well with this pattern.
For React, it might look something like this:
// src/server.ts
import { renderToString } from 'react-dom/server';
import { App } from '~/app.tsx';
export default {
fetch(req: Request) {
const html = renderToString(<App />);
return new Response(html, {
headers: {
'Content-Type': 'text/html',
},
});
},
};
Requirement #3 - a routes file, `src/routes.tsx`
Your routes file should export a routes
object. By default @ssrx/vite
expects the routes
object to conform to the
following shape:
type Route = {
// path must adhere to the path-to-regex syntax
path?: string;
children?: Route[];
// If lazy or component.preload point to a dynamic import, that route will be code split
lazy?: () => Promise<any>;
component?: {
preload?: () => Promise<any>;
};
};
react-router
and solid-router
both conform to this shape out of the box. You can provide your own routerAdapter
if
your routes config does not - see plugin-tanstack-router for an example.
Finally, update your vite.config.js
Example:
import { ssrx } from '@ssrx/vite/plugin';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
// ... your other plugins
// The plugin, with all of it's defaults.
// You only need to set these options if they deviate from the defaults.
ssrx({
routesFile: 'src/routes.tsx',
clientEntry: 'src/entry.client.tsx',
serverFile: 'src/server.ts',
clientOutDir: 'dist/public',
serverOutDir: 'dist',
runtime: 'node',
routerAdapter: defaultRouterAdapter,
}),
],
});
The ssrx
vite plugin accepts a runtime
option. The available values are:
-
node
(default) -
edge
: adjusts Vite to bundle the server output into a single file, and sets resolve conditions that are more appropriate for ssr / server rendering in popular edge environments. -
cf-pages
: adjust the output to be suitable for deployment to Cloudflare Pages, including generating sane_routes.json
and_headers
defaults.
Many thanks to these awesome libraries! Please check them out - they provided inspiration as I navigated my first Vite plugin.