Skip to content

Vite plugin

@rezejs/vite-plugin integrates the Reze compiler with Vite client builds. Install it as a development dependency alongside Vite; application code imports from reze-js.

import reze from "@rezejs/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [reze()],
});

See installation for the complete project configuration.

Options

OptionTypeBehavior
diagnostics{ jsonl?: string }Append all compiler diagnostics to this file as JSON lines. Parent directories are created when diagnostics are written.
extensionsstring[]Replace the transformed file extensions. Omitted or empty lists use the defaults.
linksstringName a module exporting link to handle compiled native anchors with href.
fileRoutesboolean or options objectEnable generated file-system routes. Requires @rezejs/router.
ssgoptions objectPre-render the application to static HTML with hydration. See static site generation.
profile{ dir: string }Directory of per-file profiling facts. The dev server files session trees posted to /__reze/profile there; later transforms read them back to specialize codegen.

The exported DEFAULT_ROUTE_EXTENSIONS contains .ts, .tsx, .js, .jsx, .mts, .cts, .mjs, and .cjs. Leading dots are optional in custom extension entries. Files in node_modules are excluded from the compiler transform.

File routes

pnpm add @rezejs/router
import reze from "@rezejs/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [reze({ fileRoutes: true })],
});

With file routes enabled, reze() returns an awaitable plugin list accepted by Vite. It serves virtual:reze-routes, whose routes and paths exports are consumed by the application's router setup.

fileRoutes optionDefaultContract
dir"src/routes"Route directory relative to Vite's root; it must exist.
types"src/routes.gen.d.ts"Generated declaration file relative to Vite's root. Set to false to disable generation.
linkstrueClaim native anchors for router navigation and active/pending attributes.
history"browser"Generate link types for browser or "hash" history. This does not construct the application's history object.
extensionsTop-level extensions, otherwise default script extensionsSelect files discovered as routes.

Keep the history option consistent with the history passed to createRouter. Browser link types account for Vite's base; hash link types use the hash prefix. The generated declarations are build output: change route files or configuration rather than editing declarations by hand.

Each route file gets a lazy chunk. Adding or removing routes during development regenerates the route module and triggers a full reload when its contents change.

Static site generation

ssg replaces client-only output with a two-target production build: the application executes against the HTML runtime to produce each page's HTML, and the browser hydrates that HTML instead of rendering from scratch. Development stays client-rendered: the dev server never executes pages at request time.

Spread children follow the same lazy insertion semantics as client rendering. The attribute-update pass does not evaluate the children getter or build a second, unused child tree.

HTML markers and transferred owner references use short page-local tokens. Hydration restores the logical owner paths before checking the layout and replaying async work; these transport tokens are not application IDs or public state.

import reze from "@rezejs/vite-plugin";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [reze({ fileRoutes: true, ssg: { entry: "src/app.tsx" } })],
});

The entry is a shared application module with one of two shapes. A router application exports the generated table, and optionally the typed builders and a shell around the matched branch:

export { paths, routes } from "virtual:reze-routes";
export { Shell as default } from "./Shell";

Without routes, the default export is the whole page and serves exactly /; combining that shape with ssg.paths is an error. Detecting the shape reads the static export graph without executing the application; ambiguous or missing exports fail the build before any page renders.

ssg optionDefaultContract
entryrequiredProject-relative application module, as above.
template"index.html"HTML template. It must contain exactly one <script type="module" src="/@reze/ssg-client.js"></script>; other scripts and styles are preserved.
selector"#app"Mount selector: # plus an ASCII identifier. The mount root must carry that id.
trailingSlash"always""always" or "never". Shapes canonical URLs; the static file layout is identical either way.
timeoutMs30000Per-page deadline: a finite number above zero. Unsettled registered work fails the page instead of emitting a placeholder.
paths{}Dynamic-leaf params: each pattern maps to param records or a one-shot callback returning them. Callbacks run once per production build.

Static leaves are enumerated automatically; each dynamic leaf pattern needs its normalized key in paths. [] skips a dynamic leaf explicitly, unknown keys or unmatched URLs fail the build, and an empty page set is an error. / is written to index.html, /a/b to a/b/index.html. The input template is not published as a separate client-only shell: without a / page, no root index.html is emitted.

Pages render sequentially, each in a fresh worker and module graph, so module-level state never leaks between URLs. Redirects become static documents with a canonical link, a meta refresh, and a small redirect reader instead of the hydration bootstrap. Any page failure — uncaught route error, unsupported transfer value, timeout, asset miss, or protocol mismatch — fails the build with its pathname.

Preprocessors and aliases apply to both targets from the same JSX input, so compiler site identities match between the HTML execution and the hydration binding. Static generation requires browser history; hash routing, library mode, build.watch, and explicit --ssr are rejected. Linked source packages outside the application root are identified by their nearest named package.json, so compiler sites and imported assets do not depend on the checkout's absolute path.

Asset URL imports and static new URL("./image.svg", import.meta.url).href expressions use the client build's asset registry during HTML execution. Vite owns assetsInclude, inlining decisions, ?inline, and ?no-inline; ?raw imports remain text. Public assets and emitted URLs respect base, including nested pages with a relative base; publicDir: false disables public-file handling. Emitted filenames are URL-encoded so spaces and Unicode also work in srcset. Missing or ambiguous client assets fail the build rather than leaking worker filesystem URLs into HTML.

Page stylesheets follow the initial app and executed route modules, including matched lazy routes. CSS belonging only to unvisited lazy routes is not added to the page.

For route naming, layouts, navigation, and application setup, see routing.

Preprocessors

A preprocessor must produce JSX before Reze transforms the file. Adding an extension does not add its parser or preprocessor.

import reze, { DEFAULT_ROUTE_EXTENSIONS } from "@rezejs/vite-plugin";

const plugin = reze({
  extensions: [...DEFAULT_ROUTE_EXTENSIONS, ".mdx"],
});

Use this configuration alongside the format's JSX-producing plugin. File-route discovery inherits the top-level extensions unless separately configured.

Diagnostics and source maps

Compiler errors fail the transform. Warnings appear through Vite. The configured jsonl file also records informational diagnostics; repeated builds append new entries.

Source-map generation follows Vite: production uses build.sourcemap; development follows dev.sourcemap. Debug names are enabled outside production. Compiler HMR support is enabled when serving and server.hmr is not false.

See compiler diagnostics for common errors and repairs.

Production hosting

Run vite build and deploy the resulting dist directory to a static host. vite preview is for local inspection of that build.

Without ssg, the build is a client-rendered application: for browser-history routing, configure the host to serve the HTML entry for application URLs that are not static files. Otherwise, opening a nested route directly can return a server 404 even when in-app navigation works.

With ssg, every page is its own static file, so serve the files directly with no fallback rewrite: rewriting an unknown path to another page's HTML would hydrate the wrong route. Unmatched URLs genuinely return 404; add a catch-all route when they should render a page instead. Match Vite's base and the browser history base when hosting under a subdirectory.

Configure the host's slash redirects to match ssg.trailingSlash. With "always", /guide must redirect to /guide/; with "never", serve the page at /guide. The hydration payload is bound to its canonical pathname, so serving a page at a different URL without redirecting is an error.

Packaged consumer smoke

packages/vite-plugin/scripts/packaged-ssg-smoke.mjs checks real tarballs outside the workspace. Local runs stage the host's native target through napi create-npm-dirs, artifacts, and pre-publish --skip-optional-publish --no-gh-release; pnpm pack resolves workspace dependencies for the JavaScript packages. The script installs into an empty temporary project with no workspace aliases or NAPI_RS_NATIVE_LIBRARY_PATH, checks exported types with external TypeScript, and builds and hydrates the fixtures through the selected Vite versions. Browser checks cover preboot content, retained nodes, dirty input preservation, and working interactions.

pnpm build
pnpm exec playwright install --with-deps chromium firefox webkit
node packages/vite-plugin/scripts/packaged-ssg-smoke.mjs --vite 8.0.0 --browsers chromium
REZE_SSG_BROWSERS=chromium,firefox,webkit node packages/vite-plugin/scripts/packaged-ssg-smoke.mjs --vite 6.4.0,7.0.0,8.0.0

--fixtures selects standalone-basics, router-full, and mdx-post; all three run by default. --artifacts-dir packages/compiler consumes an already staged compiler directory, including its platform packages, instead of rebuilding host staging. The release workflow uses this mode before publishing. Every supplied platform package is packed; only the host-compatible native binding is installed and executed. --keep retains the printed temporary directory for inspection.

The Reze plugin applies to the client and HTML build environments. Development and the default build are client-rendered; ssg adds build-time HTML execution with browser hydration.