The Tiny, Object-Based Web Component Library.
Drow is a minimalist wrapper for the Web Components API. It replaces the boilerplate of JavaScript Classes with a clean, object-based configuration. Define your components as simple objects and let Drow handle the registration and rendering.
- 🚫 Zero Dependencies: No NPM, no build steps, no headaches.
- 📉 Microscopic Size: Tiny footprint with high performance.
- 🧩 Object-First API: No more
class X extends HTMLElementorsuper(). - ⚡ Native Performance: Uses the browser's built-in Custom Elements registry.
Include drow.js in your HTML file:
<script src="drow.js"></script>Or as an NPM Module
import Drow from 'drow';
Drow components are defined using simple objects. Reactivity is handled automatically via the state object and methods.
const config = {
name: "my-counter",
state: { count: 0 },
css: `button { font-weight: bold; }`,
template: `
<div>
<button @click="increment">Count is {{count}}</button>
</div>
`,
methods: {
increment() {
this.state.count++;
}
}
};
Drow.register(config);Drow uses simple directives to handle DOM logic reactively:
| Directive | Description | Example |
|---|---|---|
@event |
Bind DOM events (click, input, etc.) | @click="doSomething" |
d-model |
Two-way binding for inputs | d-model="username" |
d-for |
Render a list of items | d-for="item in items" |
d-if |
Conditional rendering (adds/removes) | d-if="isVisible" |
d-show |
Conditional visibility (display: none) | d-show="isVisible" |
d-class:name |
Conditional CSS class | d-class:active="isActive" |
d-bind:attr |
Dynamic attribute binding | d-bind:src="imageUrl" |
This example demonstrates list rendering, two-way data binding, and event handling.
const TodoApp = {
name: "todo-app",
state: {
newTask: "",
tasks: ["Master Drow.js", "Build a tiny app"]
},
template: `
<div class="todo-box">
<h3>Task List ({{count}})</h3>
<input d-model="newTask" placeholder="Add a new task...">
<button @click="addTask">Add</button>
<button @click="clearTasks">Clear All</button>
<ul>
<li d-for="task in tasks">
<span>{{task}}</span>
<button @click="removeTask" data-item="{{task}}">x</button>
</li>
</ul>
</div>
`,
computed: {
count: (state) => state.tasks.length
},
methods: {
addTask() {
if (this.state.newTask.trim()) {
this.state.tasks = [...this.state.tasks, this.state.newTask];
this.state.newTask = ""; // Reset input
}
},
removeTask(e) {
const itemToRemove = e.target.dataset.item;
this.state.tasks = this.state.tasks.filter(t => t !== itemToRemove);
},
clearTasks() {
this.state.tasks = [];
}
},
css: \`
.todo-box { border: 1px solid #444; padding: 1rem; border-radius: 8px; }
input { padding: 5px; border-radius: 4px; border: 1px solid #ccc; }
button { cursor: pointer; background: #a78bfa; color: white; border: none; padding: 5px 10px; border-radius: 4px; }
li { display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px; }
li button { background: #ef4444; padding: 2px 8px; }
\`
};
Drow.register(TodoApp);Check out the live interactive demos:
See the docs for usage and examples:
- docs/README.md
To use Drow in your project, install it via npm:
npm install drowjsOnce installed, you can import and use it in your module-based application:
import { Drow } from 'drowjs';To contribute to Drow.js or run the project locally for testing:
npm install
npm run serverTo generate the minified version drow.min.js, use Terser:
# One-time minification
npx terser drow.js -o drow.min.js --compress --mangleAuthor johnfacey.dev
