State Management in HTML with Grains.js
I've built Grains.js. Grains.js is a lightweight framework that brings reactivity and state management to vanilla HTML, similar to Alpine.js and HTMX, but with built-in state tracking. Grains.js uses directives as a declarative approach and pure functions for state changers. Getting Started First, you need to include Grains.js into your page: To define state in Grains.js, you use the g-state directive. This automatically initializes a reactive state that can be referenced throughout your HTML. Example: Counter with Reactive State Count: Increment Decrement How It Works Define state: The g-state="counter" initializes a state variable called counter. This creates a global variable counter. If you inspect it in console as window.counter you'll see its value as Proxy(Object) {count: 0}. Whereas window.counter.count is equal to zero. Set initial value: The g-init='{"count": 0}' assigns an initial value. This can also be like g-init="myCount", which is referenced to a global variable window.myCount that you have to create. Bind state to elements: The updates dynamically when counter changes. Modify state with events: The g-on:click="increment" updates the state on button click. Define State Changers State changes are immutable and to dispatch the change we only create global pure functions. In our case increment is a global pure function, which you can inspect as window.increment. To dispatch the change we use .set method and send the new state object as an argument: function increment(ctx) { ctx.set({ count: ctx.get("count") + 1 }); } function decrement(ctx) { ctx.set({ count: ctx.get("count") - 1 }); } Take a look at minimal example to get acquainted: https://mk0y.github.io/grains.js/examples/minimal.html. All examples can be found in the repo: https://github.com/mk0y/grains.js/tree/main/examples. Why Use Grains.js? ✅ Lightweight & Fast ✅ Declarative State Management ✅ Zero Build Step ✅ Reactive Without Virtual DOM With Grains.js, managing state in HTML is intuitive and powerful, making it a great alternative for small, interactive UIs without the overhead of larger frameworks.

I've built Grains.js. Grains.js is a lightweight framework that brings reactivity and state management to vanilla HTML, similar to Alpine.js and HTMX, but with built-in state tracking. Grains.js uses directives as a declarative approach and pure functions for state changers.
Getting Started
First, you need to include Grains.js into your page:
To define state in Grains.js, you use the g-state
directive. This automatically initializes a reactive state that can be referenced throughout your HTML.
Example: Counter with Reactive State
g-state="counter" g-init='{"count": 0}'>
Count: g-text="count">
How It Works
Define state: The
g-state="counter"
initializes a state variable called counter. This creates a global variablecounter
. If you inspect it in console aswindow.counter
you'll see its value asProxy(Object) {count: 0}
. Whereaswindow.counter.count
is equal to zero.Set initial value: The
g-init='{"count": 0}'
assigns an initial value. This can also be likeg-init="myCount"
, which is referenced to a global variablewindow.myCount
that you have to create.Bind state to elements: The
updates dynamically when counter changes.
Modify state with events: The
g-on:click="increment"
updates the state on button click.
Define State Changers
State changes are immutable and to dispatch the change we only create global pure functions. In our case increment
is a global pure function, which you can inspect as window.increment
.
To dispatch the change we use .set
method and send the new state object as an argument:
function increment(ctx) {
ctx.set({ count: ctx.get("count") + 1 });
}
function decrement(ctx) {
ctx.set({ count: ctx.get("count") - 1 });
}
Take a look at minimal example to get acquainted: https://mk0y.github.io/grains.js/examples/minimal.html.
All examples can be found in the repo: https://github.com/mk0y/grains.js/tree/main/examples.
Why Use Grains.js?
✅ Lightweight & Fast
✅ Declarative State Management
✅ Zero Build Step
✅ Reactive Without Virtual DOM
With Grains.js, managing state in HTML is intuitive and powerful, making it a great alternative for small, interactive UIs without the overhead of larger frameworks.