Kit Overview
Elur Kit (@elurjs/kit) is a full-stack meta-framework built on top of
@elurjs/core. It brings file-based routing, server-side rendering, islands
architecture, content collections, and deployment adapters to Elur.
| Current version | 2.6.1 |
| Peer dependency | @elurjs/core ^4.0.5 |
| Runtime | Node >= 20.19 (Bun supported via adapter) |
| License | MIT |
@elurjs/kit@2.6.1 requires @elurjs/core@^4.0.5 — the Elur 3 range was
dropped so the package always resolves the v4 engine (with duplicate-instance
detection). Projects still on Elur 3 should stay on @elurjs/kit@2.5.x.
# Key features
- File-based routing —
page.ts,layout.ts, dynamic routes[slug], catch-all[...slug], route groups(group). - SSR + SSG + ISR — static generation, on-demand server rendering, and incremental static regeneration.
- Islands architecture — hydrate only the interactive components with
load,idle,visible, andonlydirectives. - Content collections — typed Markdown with YAML frontmatter.
- Server actions — type-safe mutations with progressive enhancement.
- Zero client JS by default — pages without islands ship 0 KB of
JavaScript (or only the router chunk); per-page gating with a
js: "legacy"escape hatch. - Next-generation client router — SPA navigation with lifecycle
events,
data-elur-persistelement survival, network-aware LRU prefetch, optional idiomorph morphing, Speculation Rules, and a loading indicator. - Streaming SSR (opt-in) — real streaming for routes with a
loading.tsboundary viadefineConfig({ streaming: true }). - Deployment adapters — Vercel, Netlify, Bun, Node.
- Observability built in — structured JSON request logging,
X-Request-IDcorrelation, andServer-Timingmetrics on every response.
# Requirements
| Requirement | Version |
|---|---|
| Node.js | >= 20.19.0 |
@elurjs/core (peer) |
^4.0.5 |
vite (peer) |
^7.0.0 || ^8.0.0 |
marked (optional peer) |
^16 || ^17 || ^18 — Markdown for content collections |
zod (optional peer) |
^4.0.0 — frontmatter/action validation |
sharp (optional peer) |
^0.33 || ^0.34 || ^0.35 — image variants |
@elurjs/vite-plugin-elur (optional peer) |
^2.2.1 — build-time compiler + HMR |
# Installation
# Scaffold a new project (recommended)
npm create elur-app@latest my-app -- --template kit
cd my-app
npm run devThe kit template ships elur-kit scripts already wired up: dev,
build, preview, start, check, routes, and doctor. Tailwind CSS
v4 is available via --tailwind.
# Add to an existing project
npm install @elurjs/core @elurjs/kit viteOptional peers, installed only if you use the feature:
npm install marked # content collections (renderMarkdown)
npm install zod # frontmatter + action input validation
npm install sharp # build-time image variants (WebP/AVIF)# Your first page
// src/app/page.ts
import { html } from "@elurjs/core";
import type { PageProps } from "@elurjs/kit";
import { load } from "./page.data.ts";
export default function HomePage({ data }: PageProps<typeof load>) {
return html`<h1>${data.title}</h1>`;
}// src/app/page.data.ts
import type { PageDataLoad } from "@elurjs/kit";
export const load: PageDataLoad = async () => {
return { title: "Hello Elur Kit" };
};Then elur-kit dev and open http://localhost:3000.
# Project structure
src/
├── app/
│ ├── layout.ts # root layout (wraps all pages)
│ ├── page.ts # home page → /
│ ├── page.data.ts # home loader
│ ├── page.action.ts # home server actions
│ ├── about/page.ts # → /about
│ ├── blog/
│ │ ├── layout.ts # nested layout for /blog/*
│ │ ├── [slug]/page.ts # → /blog/:slug (needs generateStaticParams)
│ │ └── page.action.ts # blog actions
│ ├── (marketing)/ # route group — ignored in URL
│ │ ├── layout.ts # group layout
│ │ └── pricing/page.ts # → /pricing
│ ├── api/posts/route.ts # API endpoint
│ ├── 404.page.ts # custom 404
│ └── 500.page.ts # custom 500
├── content/
│ ├── config.ts # collection definitions
│ └── blog/*.md # markdown content
├── islands/ # client-side interactive components
│ ├── LikeButton.ts
│ └── nav/MobileMenu.ts
└── middleware.ts # optional request middleware# CLI
elur-kit dev # dev server with rebuild-on-change
elur-kit build # static site build to dist/ (atomic staging, phase timings)
elur-kit preview # serve the static build in production mode
elur-kit start # SSR server — requires a previous `elur-kit build`
elur-kit adapter vercel # generate Vercel output
elur-kit adapter netlify # generate Netlify output
elur-kit adapter bun # generate Bun server
elur-kit adapter node # generate Node server
elur-kit check # typecheck + validate route/config integrity
elur-kit routes # list all discovered routes and metadata
elur-kit doctor # diagnose common config and environment issuescheck exit codes: 0 success, 1 generic error, 2 config error,
3 type error, 4 route conflict, 5 missing dependency — useful for
CI gating.
dev runs a supervisor/worker pair: the supervisor watches src/app/
and src/islands/ and restarts the Vite worker on change (400 ms
debounce, 600 ms after a crash).
Common options: --root, --app, --islands, --out, --public,
--port, --host, --lang, --hydrate-import, --router-import,
--client-config, --config, --cache-dir, --default-revalidate.
Verbosity flags (override logger.level from the config file):
elur-kit build --verbose # debug logging
elur-kit build --quiet # errors only (wins over --verbose)dev, preview, and start all run through the unified Web handler —
same redirects/rewrites, middleware, ISR cache, logging, and streaming
code path as production. start fails fast when dist/ is missing.
If the requested port is busy, the server retries on the next port (up
to 20 candidates) and prints the bound URL in the startup banner.
Note that adapter does not verify dist/ exists — run elur-kit build first so the generated output has something to serve.
# Configuration
// elur.config.ts
import { defineConfig } from "@elurjs/kit";
export default defineConfig({
output: "static", // "static" | "server" | "hybrid"
site: "https://example.com", // enables automatic sitemap.xml on build
trailingSlash: "always", // "always" | "never" | "ignore"
js: "modern", // "modern" (0% JS gating) | "legacy"
streaming: false, // opt-in streaming SSR (experimental)
router: {
enabled: true, // SPA router (false → 0 KB JS on island-free pages)
prefetch: true, // hover/focus/pointerdown prefetch
morph: false, // idiomorph DOM morphing (experimental)
speculation: "prefetch", // "prefetch" | "prerender" speculation rules
},
redirects: [{ from: "/old/:slug", to: "/blog/:slug", status: 301 }],
rewrites: [{ from: "/docs/:rest*", to: "/pages/docs/:rest" }],
headers: [{ path: "/api/*", headers: { "Cache-Control": "no-store" } }],
images: { formats: ["avif", "webp"], quality: 80 },
cache: { dir: "./.cache", adapter: myCacheAdapter }, // pluggable ISR adapter
logger: { level: "info" }, // structured request logging
security: { strictOrigin: true, bodyLimit: 1_000_000 },
});# Subpath exports
| Path | Key exports |
|---|---|
@elurjs/kit |
build, scanRoutes, island, defineConfig, loadElurConfig, renderToString, isSSR, documentShell, extractAppBody, APP_START_MARKER, APP_END_MARKER, buildHeadTags, collectShellExtras, image, processImages, processImageBatch, readManifest, writeManifest, getManifestEntry, buildSrcset, buildPictureMarkup, validateManifestUrls, isSharpAvailable, consumeImageRegistry, setImageManifest, streamBoundary, renderPage, renderErrorPage, renderPageBody, createStreamingResponse, createBufferedResponse, createSsrServer (deprecated), renderStreamingPage (deprecated), scanActions, scanIslands, generateClientEntry, buildEntrySource, buildRouterEntrySource, matchRoute, matchApiRoute, createAppManifest, writeAppManifest, writeRouteTypes, validateManifestRoutes, assertClientImportAllowed, createWebHandler, RequestContext, serveStaticFile, resolveStaticFile, incomingMessageToRequest, htmlResponse, jsonResponse, textResponse, notFound, methodNotAllowed, serverError, guessContentType, defineAction, callAction, handleActionRequest, verifyOrigin, originForbidden, fail, redirect, ActionFailure, RedirectResponse, action error cookie helpers, loadMiddleware, runMiddleware, matchesMiddleware, getCachedHtml, setCachedHtml, clearCache, StructuredLogger, createRequestLogger, vercelAdapter, netlifyAdapter, bunAdapter, nodeAdapter, startClientRouter, navigateTo, prefetch, runIntegrationHook, buildClientBundle, beginAtomicStage, copyPublicAssets |
/island |
hydrateIslands, cleanupHydratedIslands, lazyIsland, ISLAND_MARKER_ATTR, PERSIST_ATTR (island and scanIslands are root exports) |
/action |
callAction, elurJsAction, defineAction (+ ActionRequest, CallActionOptions, ElurJsAction, ActionContext, DefineActionOptions, DefinedAction, DefinedActionFn, ActionInputValidator, ActionConcurrencyMode types) |
/config |
defineConfig, loadElurConfig, ElurConfig, ResolvedElurConfig |
/content |
defineCollection, getEntry, getCollection, getEntries, renderMarkdown, renderEntryHTML, raw, parseDocument, parseFrontmatter, splitFrontmatter, createValidator, getZod, setContentRoot, withContentRoot, clearContentCache |
/seo |
generateSitemap, generateSitemapFromRoutes, generateRobots, jsonLd |
/image |
image, processImageBatch, getImage, createImageService, consumeImageRegistry, setImageManifest, isSharpAvailable, readManifest, writeManifest, getManifestEntry, buildSrcset, buildPictureMarkup, validateManifestUrls, transformHash |
/cache |
CacheAdapter, createFsCacheAdapter, createRedisCacheAdapter, createCloudflareKVCacheAdapter, getWithSWR, cacheKey, defaultInvalidator, connectCacheAdapter, CacheInvalidator, normalizeCachePolicy, shouldCachePublic, isStale |
/adapters/vercel |
vercelAdapter |
/adapters/netlify |
netlifyAdapter |
/adapters/bun |
bunAdapter |
/adapters/node |
nodeAdapter |
/router |
startClientRouter, navigateTo, prefetch, hoistStyles, ClientRouterOptions, NavigationEventDetail |
/runtime |
createWebHandler, RequestContext, serveStaticFile, resolveStaticFile, incomingMessageToRequest, sendWebResponse, htmlResponse, jsonResponse, textResponse, notFound, methodNotAllowed, serverError, guessContentType, buildSecurityHeaders, applySecurityHeaders, DEFAULT_SECURITY_HEADERS, StructuredLogger, createRequestLogger, validateCapabilities, createCapabilities, supportsStreaming, supportsPersistentStorage, supportsWritableFilesystem, DEFAULT_CAPABILITIES, SERVERLESS_CAPABILITIES, EDGE_CAPABILITIES |
/vite |
elurJsKit (Vite plugin), elurJsInterpolationPlugin |
/manifest |
createAppManifest, writeAppManifest, writeRouteTypes, validateManifestRoutes, assertClientImportAllowed |
/integrations |
runIntegrationHook, registerIntegration, getI18nIntegration, getAuthIntegration, getQueryIntegration, getTestingIntegration, getCustomIntegrations, clearIntegrations |
/cli |
CLI entry (elur-kit command) |
This very documentation site is built with Elur Kit! It uses content collections for docs, islands for interactive components, and static generation for fast page loads.
# Next steps
- Routing — file conventions, layouts, dynamic routes, SPA router, redirects
- Data & Backend — loaders, API routes, actions, middleware, metadata, cache
- SSR & Hydration —
build(),renderToString, streaming, ISR - Islands —
island(), directives,hydrateIslands,lazyIsland - Content Collections —
defineCollection,getEntry, frontmatter - Server Actions —
defineAction,elurJsAction, progressive enhancement - Middleware & Cache — middleware,
streamBoundary, cache adapters - Image & SEO —
image(),generateSitemap,jsonLd - Configuration —
ElurConfig, security, Vite plugin, integrations - Runtime & Manifest —
createWebHandler,RequestContext,AppManifest - Client router — SPA navigation,
lifecycle events,
data-elur-persist, morphing, speculation - Deployment — Vercel, Netlify, Bun, Node adapters, capabilities