JSDA-Kit — Build the Web with Pure JavaScript
A comprehensive toolkit for Static Site Generation, Server-Side Rendering, and dynamic servers — powered by standard ESM modules instead of framework magic.
The Idea
Most web frameworks demand you learn their way of doing things — proprietary file conventions, custom compilers, opaque build pipelines. JSDA-Kit takes the opposite approach: your JavaScript ESM modules are your web assets. A file named index.html.js exports a string — and that string becomes your HTML page. The same pattern works for CSS, SVG, Markdown, JSON, or anything else text-based.
This is the JSDA (JavaScript Distributed Assets) concept brought to life: PHP-like simplicity with modern JavaScript power. No template engines, no custom syntax, no black-box build steps. Just modules, template literals, and the full Node.js ecosystem at your fingertips.
// src/static/index.html.js → dist/index.html
import { getPageData } from './data.js';
const page = await getPageData();
export default /*html*/ `
<!DOCTYPE html>
<html>
<head><title>${page.title}</title></head>
<body>
<h1>${page.heading}</h1>
<app-widget></app-widget>
<script type="module" src="/app/index.js"></script>
</body>
</html>
`;
No routing config, no manifest, no plugin chain — the folder structure is the routing.
Killer Features
ESM-Native Asset Pipeline
Every .html.js, .css.js, .svg.js file is a standard JavaScript module that exports a string. You get loops, conditionals, async data fetching, and imports — not a limited template engine. Convention-based file mapping (src/static/about/index.html.js → dist/about/index.html) eliminates routing configuration entirely.
Default exports can also be functions for dynamic generation: sitemaps, RSS feeds, config-driven page sets — all from the same pipeline.
Isomorphic Web Components
JSDA-Kit integrates Symbiote.js for a three-layer rendering model:
- Server-only — rendered once during SSG/SSR, no client JS shipped
- Client-only — standard browser Web Components
- Isomorphic — set
isoMode = trueand the same component auto-detects its environment: SSR on the server, hydration on the client
import Symbiote, { html, css } from '@symbiotejs/symbiote';
class AppGreeting extends Symbiote {
isoMode = true;
greeting = 'Hello, World!';
}
AppGreeting.template = html`
<h2 ${{textContent: 'greeting'}}></h2>
`;
AppGreeting.rootStyles = css`
app-greeting {
display: block;
padding: 1em;
}
`;
AppGreeting.reg('app-greeting');
One component definition. Server renders it with Declarative Shadow DOM (optional, only if needed). Browser hydrates it seamlessly. No 'use client' / 'use server' annotations, no separate server components API, no hydration mismatches possible.
Three-Tier SSR Imports
SSR component resolution is layered for maximum flexibility:
- Global imports in
project.cfg.js— components available to every page - Per-endpoint
ssrImportsexports in individual.html.jsfiles — page-specific components - Programmatic
wcSsr()API — full control when you need it
The SSR loader automatically resolves export *, re-exports, and side-effect imports from barrel files — no manual registration boilerplate.
Trusted Types & CSP
Pass a nonce to SSR and all inline <style> tags get the nonce attribute automatically — enabling strict Content Security Policy without template hacks.
Smart Static Site Generation
JAMStack-ready SSG where source folder structure maps directly to output. The watcher mode (npx jsda ssg) rebuilds on file changes during development. Deploy the dist/ folder to any static hosting as-is — GitHub Pages, Cloudflare Pages, S3, or your own Nginx.
esbuild-Powered Build
JS and CSS bundling with tree-shaking, ESM output, and automatic tagged template minification — html and css tagged literals are minified inside bundles while untagged templates stay untouched. HTML, CSS, and SVG assets are also minified by default via @minify-html/node.
Route-Based SSR Server
A built-in dynamic server with a clear pipeline:
Request → Route Lookup → Data Injection → SSR → Minification → Response
Routes are a simple JS object mapping paths to .html.js modules. Custom hooks let you inject data per-route (getDataFn) or resolve routes dynamically (getRouteFn) — enabling i18n, A/B testing, and user-specific content with zero framework overhead.
In-memory response caching is enabled by default with URL-level granularity and per-path excludes.
Automatic Import Maps
JSDA-Kit generates <script type="importmap"> from your package.json dependencies with configurable CDN schemas, adds <link rel="modulepreload"> for critical modules, and supports optional polyfills — all automatically.
Isomorphic Utilities
A set of cross-environment helpers that work in both Node.js and the browser:
md2html()— Markdown to HTML with syntax highlighting and automatic heading IDsapplyData()— template token replacement with customizable delimitersgetHash()— SHA-1 hex via Web Crypto (browser) or Node.js crypto
Why Not Something Like Next.js?
If you've grown tired of fighting opaque framework complexity, JSDA-Kit offers a radically different path:
| JSDA-Kit | Next.js | |
|---|---|---|
| Philosophy | You control everything — ESM modules are your assets | The framework controls everything |
| Component model | W3C Custom Elements — zero runtime lock-in | React-only — components don't work outside React |
| SSR granularity | Per-component isoMode — mix rendering strategies on the same page | Per-page 'use client' / 'use server' boundaries |
| Dependencies | 7 production deps, ~50 MB node_modules | 700+ transitive deps, 300+ MB node_modules |
| Runtime weight | 0 KB static; ~7 KB gzipped for interactive | ~44 KB gzipped React + framework chunks |
| Build speed | esbuild — fast, no config, no plugins to debug | Turbopack/webpack — complex when it goes wrong |
| Codebase | ~2,500 LOC — readable, no black boxes | ~300,000+ LOC — good luck tracing a build issue |
| Vendor lock-in | None — plain files, deploy anywhere | Vercel-optimized — self-hosting has rough edges |
| Configuration | Single project.cfg.js, deep-mergeable | next.config.js + App Router + caching rules + middleware |
| CI speed | Seconds to install, seconds to build | Minutes for npm install alone |
JSDA-Kit handles projects of any complexity — from a single landing page to a full-scale web application. The difference is: you stay in control.
Quick Start
npm install jsda-kit
# Scaffold a new project
npx jsda scaffold
# Start dev server
npx jsda serve
# Start SSG watcher (dev mode)
npx jsda ssg
# Build for production
npx jsda build
The scaffold command generates a complete runnable project: folder structure, config, sample components (server-only, client-only, isomorphic), routes, a static SSG page, CSS design tokens, and dev tooling configuration.
Zero-config start — works out of the box. All settings have sensible defaults. A single
project.cfg.jsis deep-merged with defaults; override only what you need.
Architecture Highlights
JSDA-Kit stands on a few surprisingly powerful architectural decisions:
Convention over configuration, literally. The file name is the asset type. The folder path is the route. The module export is the output. There is no separate routing layer, no manifest, no plugin registry. This makes the entire system predictable and debuggable — in ~2,500 lines of code.
The ESM module as the universal unit. Every asset — HTML page, CSS stylesheet, SVG icon, Markdown article — is a JavaScript module. This means you can import one asset into another, compose pages from distributed parts, fetch data asynchronously, and publish reusable assets as npm packages with semantic versioning. Web assets become first-class citizens of the JavaScript ecosystem.
Distributed composition. Since every asset is an ESM module, you can import components from CDN endpoints, compose applications from independently deployed micro-products, and cache results at middleware boundaries. This is the natural evolution of micro-frontends — without the framework overhead.
Platform-native everything. Custom Elements for components, importmap for dependency resolution, Declarative Shadow DOM for SSR with native isolation layers, template literals for templating, JSDoc for types. Every choice leans on what the platform already provides rather than duplicating it.
Ecosystem
JSDA-Kit is part of a broader JSDA ecosystem — lightweight, interoperable tools that follow the same philosophy:
Open Source
JSDA-Kit is released under the MIT license. Contributions, feedback, and stars on GitHub are always welcome.
Free hosting friendly — the light CI pipeline fits comfortably within the free tiers of GitHub Pages or Cloudflare Pages. No paid plan required for most projects.