Why rogue
Web components are the platform's answer to UI components. Most frameworks treat them as an interop story. rogue treats them as the output — your components are custom elements, parsed and rendered by the browser itself.
The mental model in 30 seconds
If you've used React: forget the render-on-every-state-change part. In rogue, a component function runs once, when the element is mounted. State lives in signals — small observable values. JSX expressions that read those signals subscribe to them, and when a signal changes, only the affected DOM nodes update. Nothing else re-runs.
A counter looks roughly like this:
import { defineComponent, signal } from '@jjordy/rogue'
defineComponent(({ start = 0 }) => {
const [count, setCount] = signal(start)
return (
<div>
<button onClick={() => setCount(count() - 1)}>−button>
<span>Count: {count()}span>
<button onClick={() => setCount(count() + 1)}>+button>
div>
)
})
At build time, that JSX compiles into a hoisted HTML template plus
a tiny function that clones the template and wires up the two
reactive slots (the text node and the click handlers). At runtime,
clicking the + button mutates a single text node's
nodeValue — no diff, no virtual DOM, no re-render of
the surrounding tree.
The compiled output is a custom element. The filename
my-counter.jsx becomes .
Anywhere on the page that speaks HTML — server-rendered markup,
plain DOM scripts, another framework's slot — can drop in a
and it
just works.
How this differs from what you know
vs React
- Components don't re-render. No reconciliation, no virtual DOM. State changes go straight to the DOM nodes that depend on them.
- No hooks rules. Signals are just values; you can read them anywhere, conditionally, inside loops. There's no "hook order" to preserve because nothing re-runs.
-
No dependency arrays.
memoandeffecttrack what they read automatically. Add a new signal to a computation and the framework picks it up. -
The output is a native element. Inspect it in
DevTools as a real
. It has a shadow root, observed attributes, lifecycle callbacks — all the platform primitives, not framework-flavored substitutes.
vs Solid
If you know Solid, rogue's reactivity model will feel familiar —
same one-shot component renders, same signal-and-effect primitives,
same For for keyed lists. The differences are mostly
about output and ecosystem:
- Output is web components. Solid renders to regular DOM; rogue renders to custom elements with shadow DOM.
-
Smaller surface. Five public entry points total.
No
createResource, noSuspense— file-system routing + loaders cover the data-fetching story directly. - Hand-rolled compiler on acorn. No Babel, no plugin maze.
vs Lit / Stencil / other web-component frameworks
- JSX in, custom element out. No tagged template literals, no manual class-based component definitions.
- Fine-grained reactivity. Lit re-renders the whole template on state change and diffs. rogue updates the specific node the signal feeds.
- A framework, not just a renderer. File-system routing, SSR with real hydration, forms with server actions, type codegen per route — all in one tiny runtime.
The three things we keep small
- The runtime. Signals, effects, a Scope class that owns cleanup, and ~30 lines that turn compiled IIFEs into custom-element registrations. ~4.5 kB gzipped for a typical multi-page app.
-
The compiler. JSX → a hoisted
+ an IIFE that clones it and binds dynamic slots by precomputed path. No diff. No keys (unless you ask for keyed lists with). Built on acorn, not Babel. - The contract surface. Five public entry points — everything else is internals you don't have to think about.
What it doesn't try to do
- Replace your CSS framework. Use whatever. Shadow DOM gives you per-component scope for free.
- Diff anything. Reactivity decides what changes; the framework just changes it.
- Be a meta-framework. Vite plugin + tiny runtime + SSR helper. The pieces compose; we don't ship a CLI.
- Hide the platform. Components are the platform. You can pass them around, slot them, inspect them in DevTools, render them from the server with one of the browser's new HTML features (Declarative Shadow DOM).
The trade
You buy into web components: shadow DOM (styles don't leak in or out without you asking), the custom elements registry (one definition per tag, globally), attribute observation (primitive prop-passing happens through HTML attributes). In return you get something that is unusually small, unusually inspectable, and unusually fast — because the browser does the bulk of the work and we just bind the reactive bits.
If that trade sounds right for what you're building, the quickstart takes about a minute.