StunkStunk
StunkStunk
Latest release
v3.0.0-beta
v2 docs↗
DocsGitHub
Introduction
UtilitiesFAQMigration Guide

Introduction

Stunk - A lightweight, framework-agnostic state management library built on atomic chunk principles.

Stunk is a lightweight, reactive state management library built on atomic principles. Instead of one giant global store, you break state into small, independent pieces called chunks — each one self-contained, reactive, and composable.

No reducers. No boilerplate. No magic. Just state.

import { chunk } from "stunk";

const count = chunk(0);

count.get(); // 0
count.set(1); // set directly
count.set((n) => n + 1); // or derive from previous
count.peek(); // read without tracking dependencies
count.reset(); // back to 0

Stunk is framework-agnostic at its core. React, Vue, Svelte, Solid, or vanilla JS — it works everywhere. Official React integration ships with the package via stunk/react.

How it works

Every piece of state in Stunk is a chunk — a tiny reactive container that holds a single value. Chunks can be read, updated, subscribed to, derived from, and composed together.

import { chunk, computed } from "stunk";

const price = chunk(100);
const quantity = chunk(3);

// Derived from a single chunk — read-only
const discounted = price.derive((p) => p * 0.9);

// Computed from multiple chunks — auto-tracks dependencies via .get()
const total = computed(() => price.get() * quantity.get());

total.get(); // 300 — updates automatically when price or quantity changes

Think of chunks as the atoms of your application state. Small, focused, and composable.

computed() in v3 automatically discovers its dependencies by tracking which chunks call .get() during execution — no dependency arrays needed. Use .peek() inside a computed function to read a value without tracking it.

Key Features

Atomic State

Break state into independent chunks. No global store, no coupling.

Fine-Grained Reactivity

Only the components that depend on a chunk re-render when it changes.

Derive & Computed

Derive state from one chunk or compose across multiple with auto dependency tracking.

Async & Query

Built-in loading, error, caching, deduplication, and pagination via stunk/query.

Middleware

Extend chunks with logging, persistence, validation and more.

Time Travel

Undo and redo state changes with built-in history management.

React in 30 seconds

If you're using React, the stunk/react integration gives you hooks that are reactive out of the box:

import { chunk } from "stunk";
import { useChunk } from "stunk/react";

const counter = chunk(0);

function Counter() {
  const [count, setCount] = useChunk(counter);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount((n) => n + 1)}>Increment</button>
    </div>
  );
}

The component re-renders only when counter changes — nothing else.

Async in 30 seconds

The stunk/query subpackage brings a full async state layer — loading states, error handling, caching, request deduplication, and pagination — with no extra dependencies:

import { asyncChunk } from "stunk/query";

const userChunk = asyncChunk(async ({ id }: { id: number }) => fetchUser(id), {
  key: "user", // deduplicates concurrent requests
  keepPreviousData: true, // no UI flicker on param changes
  onSuccess: (data) => console.log("Loaded", data),
});

userChunk.setParams({ id: 1 });
// { loading: true, data: null, error: null }
// { loading: false, data: { id: 1, name: "..." }, error: null }

Installation

npm install stunk
pnpm add stunk
yarn add stunk
bun add stunk

What's next?

Installation

Set up Stunk in your project

Core Concepts: Chunks

Understand the foundation of Stunk state

Installation

Get Stunk up and running in your project in under a minute.

On this page

How it worksKey FeaturesReact in 30 secondsAsync in 30 secondsInstallationWhat's next?