Vite Plugin
@effex/vite-plugin provides three capabilities:
- SSR dev server — intercepts requests in dev mode, renders pages with HMR
- Server-code stripping — removes loader and handler bodies from client builds
- SSG build hook — runs
buildStaticSiteafter the SSR build completes
You only need this plugin when using @effex/platform for SSR or SSG. Pure SPAs don’t need it.
Installation
pnpm add -D @effex/vite-plugin
Configuration
// vite.config.ts
import { defineConfig } from "vite";
import { effexPlatform } from "@effex/vite-plugin";
export default defineConfig({
plugins: [
effexPlatform({
entry: "src/entry.ts",
mode: "ssr", // or "ssg"
}),
],
});
Options
| Option | Type | Default | Description |
|---|---|---|---|
entry |
string |
— | Path to the SSR/SSG entry module |
mode |
"ssr" | "ssg" |
"ssr" |
Build mode |
include |
RegExp |
/\.(tsx?|jsx?)$/ |
Files to apply server-code stripping to |
exclude |
RegExp |
— | Files to exclude from stripping |
SSR Dev Server
When entry is provided and Vite is in dev mode, the plugin intercepts incoming requests and renders them through your entry module:
// src/entry.ts (SSR mode)
import { HttpApp, HttpRouter } from "@effect/platform";
import { Platform } from "@effex/platform";
import { App } from "./app.js";
import { router } from "./routes.js";
const effexRoutes = Platform.toHttpRoutes(router, {
app: App,
document: { title: "My App", scripts: ["/src/client.ts"] },
});
const httpApp = HttpRouter.empty.pipe(HttpRouter.concat(effexRoutes));
const handler = HttpApp.toWebHandler(httpApp);
export async function render(request: Request): Promise<Response> {
return handler(request);
}
The entry must export a render function that takes a Web Request and returns a Web Response. The plugin:
- Converts the incoming Node request to a Web Request
- Calls your
renderfunction viavite.ssrLoadModule(with HMR) - Injects Vite’s HMR client into HTML responses
- Sends the response back to the browser
Static assets, Vite internal requests, and source files are passed through to Vite’s normal middleware.
Server-Code Stripping
In client builds, the plugin removes server-only code so it doesn’t end up in the browser bundle:
| Source | Client build output |
|---|---|
Route.get(loader, render) |
Route.get(null, render) |
Route.post("key", handler) |
Route.post("key", () => { throw new Error("server only"); }) |
Route.put("key", handler) |
Same as post |
Route.delete("key", handler) |
Same as post |
Route.static({ paths, load, render }) |
Route.render(() => (render)(undefined)) |
This means your loader functions can import server-only dependencies (database clients, filesystem, etc.) without worrying about them being bundled into the client. The plugin also removes import declarations that become unused after stripping.
Stripping only happens in client builds — SSR builds and dev mode keep the full code.
SSG Build Hook
In SSG mode, the plugin runs Platform.buildStaticSite after the SSR build completes:
effexPlatform({ mode: "ssg", entry: "src/entry.ts" })
The build sequence is:
vite build— client bundle (stripping applied, SSG hook skipped)vite build --ssr src/entry.ts— SSR bundle (no stripping, SSG hook runs)
The hook imports the compiled SSR entry from the output directory and calls buildStaticSite with the exported router, app, document, and layers.
SSG Entry Point
// src/entry.ts (SSG mode)
import { App } from "./app.js";
import { router } from "./routes.js";
export { router };
export const app = App;
export const document = {
title: "My Site",
scripts: ["/assets/client.js"],
styles: ["/assets/styles.css"],
};
// Optional: services needed by loaders at build time
export { layers } from "./services.js";
The entry can also export a render function for the dev server — both SSR and SSG entries work in dev mode.
Build Scripts
SSR
{
"scripts": {
"dev": "vite",
"build": "vite build && vite build --ssr src/entry.ts",
"start": "node dist/server.js"
}
}
SSG
{
"scripts": {
"dev": "vite",
"build": "vite build && vite build --ssr src/entry.ts",
"preview": "vite preview"
}
}
Both modes use the same two-step build. The difference is what happens after: SSR deploys a server, SSG deploys static files.