Add interactivity to plain HTML with js- directives.
Fine-grained signal reactivity, zero build step, CSP-safe.
<script src="https://unpkg.com/signet.js" defer init></script>
<div js-scope="{ count: 0 }">
<p js-text="'Count: ' + count"></p>
<button js-on:click="count++">Increment</button>
</div>
Powered by @preact/signals-core. Only the exact DOM nodes that depend on a changed value are updated â no virtual DOM diffing.
The default entry point uses a recursive descent parser. No eval() or new Function(). Safe under strict Content-Security-Policy headers.
Around 15KB minified (~5KB gzip) including @preact/signals-core. An alternate signet.js/unsafe entry point skips the CSP parser for an even smaller footprint.
Add js-text, js-on:click, js-if and others to existing HTML. No templates, no compilation, no framework CLI.
All js- attributes are stripped from the DOM after binding. The rendered output is clean, semantic HTML with no framework residue.
Nest js-scope elements to create local state. Child scopes inherit parent properties via JavaScript's prototype chain.
Alpine.js, petite-vue, and Stimulus are excellent tools. Signet.js is for when you need a handful of interactive elements in an otherwise server-rendered page â without a build pipeline, without learning a component model.
Instead of diffing a virtual DOM tree on every state change, Signet.js uses fine-grained signals from @preact/signals-core. Only the DOM nodes that read a changed signal are updated, precisely and automatically.
The library is written in plain, readable ESM modules with no magic you can't understand in an afternoon. Pull it apart, fork it, or embed it directly â it's just JavaScript.
The init attribute triggers auto-discovery of all top-level js-scope elements. No JavaScript required.
<!-- Auto-initialises all js-scope elements -->
<script src="https://unpkg.com/signet.js"
defer init></script>
<div js-scope="{ name: '' }">
<input js-model="name"
placeholder="Your name" />
<p js-show="name.length > 0"
js-text="'Hello, ' + name + '!'">
</p>
</div>
Use createApp() when you need computed properties, methods, custom directives, or a global store.
import { createApp } from 'signet.js';
createApp({
name: '',
get greeting() {
return this.name
? 'Hello, ' + this.name + '!'
: '';
},
submit() {
alert(this.greeting);
},
})
.mount('#app');