JUST DOMJUST-DOM

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):

  1. TypeScript or JavaScript (default: TypeScript)
  2. CSS framework (one of): none, Tailwind CSS v4, or Tailwind v4 + DaisyUI
  3. 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:

FlagMeaning
--ts, --typescriptVite vanilla-ts template
--js, --javascriptVite vanilla template (.js entry + jd.config.js)
--plugins=<list>Comma-separated plugin ids: router, signals, lucide
--no-pluginsInstall just-dom only
--css=<option>none, tailwind (Tailwind v4 + @tailwindcss/vite), or tailwind-daisyui
--no-cssSame as --css=none
--pnpmUse pnpm add inside the new folder instead of npm install
-y, --yesSkip prompts
-h, --helpPrint 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 --pnpm

From this monorepo while developing the CLI:

pnpm create:app ../path/to/my-app

The 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.

See also

On this page