App setup (jd.config)
One module for just-dom, plugins, and a typed jd export—used from main and every component
Just DOM does not load a magic config file at runtime (unlike Next.js reading next.config.ts). What teams usually want is the same idea: a single app module—often named jd.config.ts (TypeScript) or jd.config.js (JavaScript)—where you call withPlugins once, then re-export the configured object as jd. Your entry (main.ts / main.js) and every UI module import jd from there, like a small shared runtime.
Start a new project
Use the create-just-dom CLI to generate a Vite app with jd.config and a working entry file:
npm create just-dom@latest
npm create just-dom@latest my-app
npm create just-dom@latest . # current directory (must be empty)In an interactive terminal you get three prompts in this order (keyboard: ↑/↓, Space, Enter):
- TypeScript or JavaScript (default: TypeScript)
- CSS framework (one of): none, Tailwind CSS v4, or Tailwind v4 + DaisyUI
- Plugins (multi-select):
@just-dom/router,@just-dom/signals,@just-dom/lucide— all optional
Non-interactive / CI: pass --yes to skip prompts (defaults: TypeScript, no plugins, no CSS framework), or set each option explicitly:
| Flag | Meaning |
|---|---|
--ts, --typescript | Vite vanilla-ts template |
--js, --javascript | Vite vanilla template (.js entry + jd.config.js) |
--plugins=<list> | Comma-separated plugin ids: router, signals, lucide |
--no-plugins | Install just-dom only |
--css=<option> | none, tailwind (Tailwind v4 + @tailwindcss/vite), or tailwind-daisyui |
--no-css | Same as --css=none |
--pnpm | Use pnpm add inside the new folder instead of npm install |
-y, --yes | Skip prompts |
-h, --help | Print all flags |
Examples:
npm create just-dom@latest my-app -- --yes
npm create just-dom@latest my-app -- --js --no-plugins --no-css
npm create just-dom@latest my-app -- --ts --plugins=router --css=tailwind-daisyui --pnpmFrom this monorepo while developing the CLI:
pnpm create:app ../path/to/my-appThe tool invokes npm create vite@latest with --no-interactive so Vite does not prompt during scaffolding. Choosing Tailwind updates vite.config, src/style.css, and the starter main markup accordingly. Unknown --plugins or --css values are ignored with a warning (see terminal output).
jd.config.ts
import DOM, { withPlugins } from "just-dom";
import { createRouterPlugin } from "@just-dom/router";
const router = createRouterPlugin();
/** Your app’s configured DOM (tags + all plugins). */
export const jd = withPlugins(DOM, [router]);
/** Use this type in helpers when you need “DOM + plugins” typing. */
export type Jd = typeof jd;Add more entries to the same array for other official plugins (for example @just-dom/lucide) or your own definePlugin modules.
Keep this file small and one-way: only just-dom, plugins, and optional shared constants. Do not import main.ts, route tables, or screens that themselves import jd from here—otherwise you risk circular imports and undefined initialization order.
main.ts (entry)
import { createRoot } from "just-dom";
import { jd } from "./jd.config";
const app = jd.div({ className: "app" }, [
jd.h1({}, ["Hello"]),
]);
createRoot("app", app);Components (import jd, not DOM)
import { jd } from "../jd.config";
export function UserCard(name: string) {
return jd.article({ className: "card" }, [
jd.h2({}, [name]),
jd.lucide("House", { size: 18 }),
]);
}This is the closest analogue to “always having React in scope”: your configured jd is the stable import from your module instead of from just-dom alone.
Why not pass jd as an argument everywhere?
You can—function UserCard(jd: Jd, name: string) is fine for libraries or tests. For app code, a singleton module is usually simpler:
- Less boilerplate on every call site.
- Same pattern as a configured HTTP client or logger.
- TypeScript still infers everything if you export
export type Jd = typeof jd.
The factory style (homePage(jd)) appears in docs only when you want zero runtime import of jd.config from a file (for example to break a circular dependency). If jd.config never imports your pages, you can always use import { jd } from "./jd.config" instead.
Official plugins
Register plugins only in jd.config.ts. Feature docs (@just-dom/router, @just-dom/lucide, …) stay focused on the plugin API; this page is the project-level convention for wiring them together.