DruidUI
DruidUI is a React-like framework that compiles to WebAssembly for building sandboxed user interfaces. Use it when you need to run untrusted UI code safely.
Why WebAssembly?
Traditional approaches can't provide true sandboxing:
- JavaScript - Can access all browser APIs
- iframes - Limited state sharing, poor UX
WebAssembly provides complete isolation - code can only access explicitly granted functions.
Quick Start
npx create-druid-ui my-app
cd my-app
npm install
npm run dev
Hello World
import { createComponent, Context } from '@druid-ui/component';
let count = 0;
export const component = createComponent((ctx: Context) => {
return (
<main>
<h1>Hello Druid!</h1>
<button onClick={() => { count++; }}>
Clicked {count} times
</button>
</main>
);
});
Build & Deploy
# Build to WASM
npm run build # → dist/component.wasm
# Add to scroll
ui:
- name: dashboard
path: ui/dashboard.wasm
route: /admin
Core Concepts
Functional Components
Similar to React and Mithril.js:
- JSX/TSX syntax
- Every event triggers full rerender (no complex diffing)
- Module-level state (no hooks)
let items = ['todo 1', 'todo 2'];
export const component = createComponent(() => {
return (
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
);
});
Context
Access route data via context:
export const component = createComponent((ctx: Context) => {
return (
<div>
<p>Path: {ctx.path}</p>
<p>User ID: {ctx.params.userId}</p>
</div>
);
});
Shadow DOM
Each component renders in isolated Shadow DOM - styles can't leak in/out.
Development Workflow
Raw Mode (Fast)
Develop without WASM overhead:
<druid-ui no-sandbox src="/src/index.tsx"></druid-ui>
- Instant hot reload
- Normal browser debugging
- Not sandboxed (dev only!)
Sandbox Mode (Production)
Full WASM sandboxing:
npm run build
npm run preview
- True isolation
- Production-ready
- Slower iteration
Extension System
By default, WASM can only call d(), log(), and rerender(). Add custom APIs via extensions:
1. Define interface (WIT):
package my:api;
interface fetch {
get-data: func(url: string) -> string;
}
2. Implement in host:
druidElement.extensionObject = {
'my:api/fetch': {
getData: async (url) => {
const res = await fetch(url);
return res.text();
}
}
};
3. Use in component:
import { getData } from 'my:api/fetch';
const data = await getData('/api/status');
See package documentation for full API reference.
Multi-Language Support
Current: JavaScript/TypeScript ✅
Planned: Rust, C++ (waiting for WebAssembly Component Model maturity)
Limitations
- No async/await yet (use callback workaround)
- Full rerenders on every event (less efficient than React)
- JavaScript only for now
- No SSR
Learn More
- NPM Packages - API reference
- Examples
- Source Code