Mini React-like engine: JSX, virtual DOM, diffing, and hooks.
SandJS is a lightweight educational rendering engine written in vanilla JavaScript. It helps you understand how modern UI frameworks work internally by implementing:
- JSX to virtual DOM transformation
- Initial DOM mount
- Tree diffing and patching
- Local state with a basic
useStatehook - Simple hot reload workflow
The project is intentionally minimal and readable, so each core concept is easy to inspect.
Sand.createElement(type, props, ...children)for JSX runtime support- Recursive DOM creation (
createDom) for first render - Incremental updates via
patch(oldVNode, newVNode) - Component function support
- Hook state storage per component path
- Event and prop syncing to real DOM
sand/sand.js: core rendering engineapp/App.js: demo app and test componentshotReload.js: development hot-reload helperindex.html: runtime entry and page shell
- A modern browser
- No build tool required
Open index.html directly in your browser.
If you use a local server (recommended for smoother development), run for example:
cd sandjs
python3 -m http.server 8080Then open http://localhost:8080.
/** @jsx Sand.createElement */
function App() {
const [count, setCount] = Sand.useState(0);
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
Sand.render(<App />, document.getElementById("root"));Unit tests cover the core engine (createElement, initial mount, useState and DOM updates on re-render), running with Vitest in a jsdom environment:
npm install
npm testThe test suite also exposed that the global hook store is shared across roots — tests reset it between runs via a small test-only helper, so the engine file itself stays untouched.
- Child reconciliation is index-based (no robust keyed diff yet)
- No
useEffect/lifecycle hooks - No batching/scheduler for state updates
- Full root render trigger on each state update
Suggested next improvements:
- Add keyed reconciliation for list stability.
- Improve prop/style cleanup on updates.
- Implement update batching with microtasks.
- Add hook extensions (
useEffect,useMemo). - Add keyed reconciliation for list stability (tests already cover mount/patch/hook).
Contributions are welcome. Keep changes small, focused, and documented so the educational value remains high.
MIT — see LICENSE.