statekit-lite
v1.3.1
Published
Minimal proxy-based global state manager for React with nested access, persist, watch, devtools, and realtime sync via plugins.
Maintainers
Readme
🧠 statekit-lite
A minimal global state manager for React
- 🔥 has no dependencies (except immer)
- ⚛️ Fully typed reactive access
- 🔁
.get(),.set(),.use()and.watch()on any nested path - 📦 Redux DevTools compatible
- 🌐 Realtime support with SSE plugin
✨ Key Features
🔥 Proxy-based access with automatic nested structure creation
🔥 Typed access and reactivity with
.get()/.set()/.use()📝 Watch outside React with
.watch(fn)💾 Persist to localStorage
⚛️ Redux DevTools integration
✨ SSR-safe by design
🌐 Realtime updates via ssePlugin()
🧩 Plugin system — extend behavior with middleware-style plugins
🌐 Realtime sync via:
ssePlugin()– Server-Sent EventssyncPlugin()– universal sync layer (WebSocket, polling, etc.)supabasePlugin()– Supabase integration (with optional fallback polling)
📦 Installation
# Using npm
npm install statekit-lite
# Using yarn
yarn add statekit-lite
# Using pnpm
pnpm add statekit-lite📌 Examples
crate state
import React from "react";
import { createStore } from "statekit-lite";
// Create store with persist + devtools
const userStore = createStore({
user: {
name: 'Anon',
age: 25
}
}, {
persist: {
key: 'user' // (path key from local storage) persist save, and auto load
},
immer: true
});
full example of possible use
import React from "react";
import { createStore } from "statekit-lite";
// Create store with persist + devtools
const userStore = createStore({
user: {
name: 'Anon',
age: 25
}
}, {
persist: {
key: 'user'
},
devtools: {
name: "userStore"
},
immer: true
});
// Component that shows state
function Display() {
const user = userStore.user.use();
return (
<div style={{ marginLeft: '45%', marginTop: '15%', fontSize: '24px', color: 'silver' }}>
{user.age}
</div>
);
}
// Component that updates state every second
function Updater() {
React.useEffect(() => {
const i = setInterval(() => {
userStore.user.arr[1].set({ t: 1 }); // creates nested array structure
userStore.user.test.test.set({ a: 1 }); // creates nested object structure
userStore.user.age.set(age => age + 1); // updates with function
console.log(userStore.user.get());
}, 1000);
return () => clearInterval(i);
}, []);
return null;
}
// Root App
export function App() {
return (
<div>
<Updater />
<Display />
</div>
);
}✅ Explanation
createStore(...)creates a globally accessible reactive storeuserStore.user.age.use()subscribes to changes and re-rendersDisplayset(...)auto-creates nested paths likearr[1]ortest.testpersistkeeps state across reloads using localStoragedevtoolslogs each.set()call into Redux DevTools
✅ watch(fn) — programmatic change listener
userStore.user.watch((user) => {
console.log("state changed: ", user);
});🧩 Plugins
statekit-lite supports realtime sync via plugins.
🔄 syncPlugin(options)
A universal plugin that connects any part of the store to remote data (SSE, WebSocket, polling, etc.).
✅ Features
- Reactive sync from remote
- Optional pushUpdate to server
- Works on any nested path
- Supports both full-replace and updater modes
import { syncPlugin, createStore } from 'statekit-lite';
const store = createStore({ user: { name: '', age: 0 } }, {
plugins: [
syncPlugin({
subscribe: (emit) => {
const source = new EventSource('http://localhost:3000/events');
source.onmessage = (e) => emit(JSON.parse(e.data));
return () => source.close();
},
pushUpdate: (data) => {
fetch('/update', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'Content-Type': 'application/json' },
});
},
debug: true,
}),
]
});or ssePlugin
Convenience wrapper around syncPlugin for Server-Sent Events (SSE). Enable realtime updates from a server:
import { createStore, ssePlugin } from 'statekit-lite';
const store = createStore({
messages: [] as string[],
}, {
plugins: [
ssePlugin<string>({
url: 'http://localhost:3000/events',
path: ['messages'],
mode: 'push',
mapper: (data) => data.message
})
]
});
// example component real time update SSE
function Messages() {
const list = store.messages.use();
return <ul>{list.map((msg, i) => <li key={i}>{msg}</li>)}</ul>;
} type SSEPluginOptions<T> = {
url: string; // 🔌 URL SSE endpoint
path?: (string | number)[]; // 🔑 (optional) Path inside store to update
mapper?: (data: any) => T; // 🧠 (optional) transform before storing
mode?: 'set' | 'push'; // 🔁 (optional) 'set' (default) or 'push' to array (push mode is ideal for appending to arrays, set to override the target value)
}
🔌 Realtime Server Example (Node.js + Express)
Below is a minimal SSE backend you can use to push real-time updates into statekit-lite.
💡 → Example of use ←
// server.ts
import express from 'express';
import cors from 'cors';
const app = express();
app.use(cors());
app.use(express.json());
let clients: Response[] = [];
// SSE endpoint: clients connect here
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
clients.push(res);
console.log('👤 Client connected');
req.on('close', () => {
clients = clients.filter(c => c !== res);
res.end();
console.log('❌ Client disconnected');
});
});
// Send an event to all clients
app.post('/send', (req, res) => {
const msg = req.body?.message ?? 'Пустое сообщение';
const payload = JSON.stringify({ data: msg });
for (const client of clients) {
client.write(`data: ${payload}\n\n`);
}
res.sendStatus(200);
});
app.listen(3000, () => {
console.log('🚀 SSE server running at http://localhost:3000/events');
});🗄️ supabase Plugin
A plugin that synchronizes your entire store with a Supabase table in key-value format.
Ideal for:
- Realtime collaboration
- Shared persistent state across clients
- Saving editor/project/user states per session or user ID
To use supabasePlugin, install Supabase client:
npm install @supabase/supabase-js✅ Features
- Bidirectional sync with Supabase (
jsonb) - Works with any key and field (custom primary key supported)
- Realtime updates using
postgres_changes - Auto-insert on first load
- Full store hydration and push on change
🧩 Table structure
create table kv_store (
key text primary key,
value jsonb,
updated_at timestamp default now()
);⚠️ Enable Realtime in Supabase
To receive realtime updates from Supabase, you must explicitly enable Realtime for your table.
- Go to your project in Supabase Dashboard
- Navigate to Table Editor → kv_store
- Click on the Realtime tab
- Toggle the switch to Enable Realtime
Otherwise, .on('postgres_changes', ...) will not trigger any events.
🔧 Usage
import { createStore, supabasePlugin } from 'statekit-lite';
const store = createStore({ count: 0 }, {
plugins: [
supabaseKVPlugin({
url: 'https://your-project.supabase.co',
anon_key: 'your-anon-key',
table: 'kv_store',
key: 'session-123', // identifier of this row
field: 'value', // optional (default = 'value')
primary_key: 'key', // optional (default = 'key')
debug: true,
polling: 3000, // ← (optional) fallback polling every 3s if Realtime is not working
})
]
});
This plugin automatically:
- Loads the initial state from Supabase
- Subscribes to realtime changes for the same key
- Pushes new state on every .set() or .update()
✅ Supports multi-user setups (just change the key value per user/project)
🧩 When to Use
- Global state in React SPA
- Forms, visual editors, configuration panels
- Embedded apps and UI libraries
- Minimal, fast alternative to Redux/Zustand
- Real-time dashboards, chats, logs (via SSE plugin)
- Server-driven UIs or status syncing
