Upgrade Guide
# Upgrading to v2.4.4
v2.4.4 fixes two bugs introduced in v2.4.3 that prevented client-only islands from hydrating on the client.
# Bugfix-only release
v2.4.4 is a bugfix release. No API changes — just upgrade:
bun add @deijose/nix-js-kit@^2.4.4# What was broken in v2.4.3
directive: "only"never hydrated —hydrateIslands()only had branches for"load","idle", and"visible". Markers withdata-directive="only"were silently skipped — the component never mounted, leaving the fallback HTML (or empty marker) permanently.hydrateTemplatedid nothing for islands without SSR DOM — Islands with"only"orssr: falsehave no SSR-rendered DOM in the marker (only fallback HTML or nothing).hydrateTemplatewalks existing DOM for hydration markers — when there's nothing to walk, it silently does nothing (templates with no bindings) or throws a mismatch and remounts (templates with bindings).
# What v2.4.4 does
"only"now hydrates immediately like"load"inhydrateIslands().- The hydrator detects the absence of
<!--nix-markers and does a fresh_rendermount instead ofhydrateTemplate— the correct operation when there's no SSR DOM to hydrate against.
# If you applied a workaround in v2.4.3
If you used island("Name", Component, props, "load", { ssr: false })
instead of "only" as a workaround, you can now switch back to
island("Name", Component, props, "only") — both work correctly in
v2.4.4. The workaround also continues to work.
# Upgrading to v2.4.3
v2.4.3 fixes a regression introduced in v2.4.0: island() crashed for
client-only components after the happy-dom fallback was removed. It also
adds two opt-out mechanisms for client-only islands and an isSSR()
guard for environment reads.
# No breaking changes
All additions are opt-in and backward compatible:
directive: "only"is a new value (default remains"load").optionsis a new optional 5th parameter.isSSR()is a new export.- Existing
island("Name", Component, props, "load")calls work unchanged.
# Migrating client-only components
If you had components that accessed document/window/navigator and
worked before v2.4.0 (because happy-dom provided a mock), they now
crash with ReferenceError: document is not defined. Fix them with one
of:
// 1. directive: "only" — skip SSR, hydrate on load
island("Carousel", Carousel, {}, "only")
// 2. options: { ssr: false } — skip SSR, keep any directive
island("Chart", Chart, { data }, "visible", { ssr: false })
// 3. isSSR() — guard environment reads (keeps SSR fallback HTML)
import { isSSR } from "@deijose/nix-js-kit";
function ThemeToggle() {
const dark = signal(isSSR() ? false : matchMedia("(prefers-color-scheme: dark)").matches);
// ...
}
island("ThemeToggle", ThemeToggle, {}, "load")isSSR() only works for environment reads (window.matchMedia,
localStorage, navigator). It does not work for
document.querySelectorAll(".slide") of the component's own children —
the DOM is not inserted when the function body runs. For that, use
NixComponent.onMount() + ref, or directive: "only".
# Fallback content
Use options.fallback to show loading content while the client-only
island hydrates:
island("Carousel", Carousel, {}, "only", {
fallback: html`<div class="skeleton">Loading…</div>`,
})# Upgrading to v2.4.0
v2.4.0 removes happy-dom entirely, renames the config file, and fixes CLI bundling bugs.
# Breaking changes
happy-domfully removed — the kit no longer references happy-dom anywhere. SSR uses the core's DOM-freerenderToString(@deijose/nix-js/server) directly, with no DOM fallback. If you hadhappy-domin your dependencies for SSR, you can remove it.- Config file renamed:
nix.config.*→nix-js.config.*— the genericnix.config.tsname could collide with other tools. The kit now looks fornix-js.config.{ts,js,mjs}first. Legacynix.config.*files are still detected but emit a deprecation warning.
# Migration
# Rename config file
mv nix.config.ts nix-js.config.ts
# Remove happy-dom if you had it installed for SSR
bun remove happy-domRun nix-js-kit doctor to check for legacy config files and other issues.
# Fixes
- CLI bundling bug where the image registry had a dual instance (CLI bundle vs library chunk), causing
consumeImageRegistry()to always return[]. raw()missingrenderServer— caused "Template does not support server rendering" errors after happy-dom removal.
# Upgrading to v2.3.0
v2.3.0 delegates template interpolation to the Vite plugin when available.
# No breaking changes
bun add @deijose/nix-js-kit@^2.3.0# What changed
When @deijose/vite-plugin-nix-js >= 1.1.0 is installed, the kit's legacy interpolation transform is skipped (the plugin's state-machine lexer takes precedence). The kit's transform remains as a fallback for projects that use the kit without the plugin.
# Migration
Install the Vite plugin for the best interpolation support:
bun add @deijose/vite-plugin-nix-js@^1.1.0# Upgrading to v2.2.0
v2.2.0 adds native partial attribute interpolation when using Nix.js core >= 3.3.
# No breaking changes
bun add @deijose/nix-js-kit@^2.2.0# What changed
When the installed Nix.js core exposes templateFeatures.partialAttributeInterpolation (core >= 3.3), the kit no longer injects the legacy nixJsInterpolationPlugin transform. Partial attributes now run through the runtime's native normalization, preserving fine-grained reactivity.
New interpolation: "auto" | "legacy" | "off" option on nixJsKit(), buildClientBundle(), and transformProjectFiles():
"auto"(default) — uses native when available, legacy otherwise"legacy"— forces the old transform for migrations (emits deprecation warning)"off"— disables interpolation transform entirely
# Upgrading to v2.1.0
v2.1.0 adds five major features: route-level code-splitting, layout slots, cache adapters, real Suspense streaming, and makes happy-dom an optional peer dependency.
# No breaking changes
bun add @deijose/nix-js-kit@^2.1.0# New features
Layout slots
*.slot.ts files are now detected by the route scanner and passed to layout components as named slots (slots.sidebar, slots.header, etc.). See Pages and Layouts.
Cache adapters
New createRedisCacheAdapter() and createCloudflareKVCacheAdapter() in @deijose/nix-js-kit/cache. See Rendering Modes — Cache adapters.
Real Suspense streaming
streamBoundary() and createStreamingResponse() now emit a <template> chunk + replacement script that swaps the fallback <div> for the resolved content in-place (using replaceWith), instead of the old innerHTML append pattern.
Route-level code-splitting
The generated client entry uses import() per island, producing separate chunks per page for a minimal initial bundle. This is automatic — no configuration needed.
# Upgrading to v2.0.x
v2.0 is a major release that adds DOM-free SSR, a unified Web handler, adapter capabilities, image pipeline hardening, island HMR, recursive content collections, and production error sanitization. It requires @deijose/nix-js@^3.0.0.
# Breaking changes
@deijose/nix-jspeer dependency — bumped to^3.0.0(DOM-free SSR, hydration markers).- SSR is now DOM-free —
renderToStringno longer uses happy-dom. Templates render directly from descriptors. Hydration markers (<!--nix-N-->,data-nix-e-*) are emitted by default. - Unified Web handler —
dev,preview,start, and all adapters now sharecreateWebHandler(). If you had custom request handling logic that duplicated the CLI pipeline, migrate tocreateWebHandlerfrom@deijose/nix-js-kit/runtime. - Production errors are sanitized —
String(err)is no longer used in responses. UsepublicErrorResponse()for custom error handling. - Log prefixes — all internal logs now use
[nix-js](core) and[nix-js-kit](kit), not[Nix].
# New features (non-breaking, opt-in)
lazyIsland()— deferred island component loading without probing.- Island HMR — Vite plugin hot-swaps island modules without full page reload.
getImage()/ImageService— programmatic single-image and runtime image transforms.- Image hardening — SHA-256 transform keys, path containment, atomic writes, single-flight,
strictmode. AdapterCapabilities— explicit host capability declarations with build-time validation.- Static Range/If-Range/HEAD — full HTTP semantics for static file serving.
- Recursive content collections —
getCollection()scans nested directories with derived slugs. - CLI commands —
check,routes,doctor. RequestContext— unified per-request context withparams,locals,cookies,signal,requestId.
# Migration steps
- Update both packages:
bun add @deijose/nix-js@^3 @deijose/nix-js-kit@^2- Run
nix-js-kit doctorto diagnose version mismatches and config issues. - Run
nix-js-kit checkto typecheck and validate routes. - Run
nix-js-kit buildto verify the build. - If you have custom servers, migrate to
createWebHandler()from@deijose/nix-js-kit/runtime.
The v1 → v2 migration guide with detailed API mapping is in docs/nix-js-kit/nix-js-kit-v1-v2-migration-guide.md (draft). The automated migration report tool is planned for a later v2.x release.
# Upgrading to v1.3.0
v1.3.0 is a minor release with new features and no breaking changes. However, if you were using internal identifiers from v1.2.6 or earlier, some were renamed for consistency.
# Naming changes (v1.2.7 → v1.3.0)
If you referenced internal identifiers in custom code, update them:
| Old | New | Location |
|---|---|---|
__nix_action_error |
__nix_js_action_error |
Action error cookie |
data-nix-head |
data-nix-js-head |
Head tag marker |
nix-stream-* |
nix-js-stream-* |
Stream boundary IDs |
These are internal identifiers. If you only use the public API (nixJsAction, island(), build(), etc.), no changes are needed.
# New optional peer dependencies
v1.3.0 adds three optional peer dependencies:
bun add marked zod sharp
# or
npm install marked zod sharpInstall them only if you use the corresponding features:
marked— Content layer (Markdown rendering)zod— Content layer (schema validation)sharp— Image optimization (build-time pipeline)
# TypeScript 7
v1.3.0 upgraded to TypeScript 7.0.2 (native Go compiler). If you haven't already:
bun add -d typescript@^7TypeScript 7 removes baseUrl from tsconfig.json. Update your paths to be relative to the config file:
{
"compilerOptions": {
"paths": {
"@deijose/nix-js-kit": ["./node_modules/@deijose/nix-js-kit/dist/lib/index.d.ts"],
"@deijose/nix-js-kit/content": ["./node_modules/@deijose/nix-js-kit/dist/lib/content/index.d.ts"]
}
}
}TypeScript 7's paths resolve relative to the tsconfig.json file location, not to baseUrl. You no longer need baseUrl at all.
# New features (non-breaking)
All new features in v1.3.0 are opt-in:
- Metadata API — export
generateMetadata()frompage.ts - Content layer — create
src/content/config.tsand.mdfiles - Image optimization — use
image()helper in pages - Middleware — create
src/middleware.ts - CSRF protection — enabled by default, configure via
actionSecurity - Prefetch — automatic in the SPA router
- View Transitions — automatic when the browser supports it
No existing code needs to change to benefit from these features.
# Upgrading from v1.2.x to v1.2.7
v1.2.7 renamed public API identifiers for consistency:
| Old | New |
|---|---|
nixAction |
nixJsAction |
NixAction (interface) |
NixJsAction |
nixKit (Vite plugin) |
nixJsKit |
NixKitViteOptions |
NixJsKitViteOptions |
Update your imports:
// Old
import { nixAction } from "@deijose/nix-js-kit/action";
// New
import { nixJsAction } from "@deijose/nix-js-kit/action";// Old
import { nixKit } from "@deijose/nix-js-kit/vite";
// New
import { nixJsKit } from "@deijose/nix-js-kit/vite";# General upgrade steps
- Check the CHANGELOG for breaking changes
- Update the package:
bun add @deijose/nix-js-kit@latest - Run
tsc --noEmitto catch any type errors - Run
nix-js-kit buildto verify the build - Run your tests