lite-zustand 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lider Bektaş
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,243 @@
1
+ # lite-zustand
2
+
3
+ A tiny (**<1KB**) type-safe state store for React. Selector-based subscriptions inside components, full state access and reactive watchers outside. Zero unnecessary re-renders, no providers, no boilerplate.
4
+
5
+ ```bash
6
+ npm install lite-zustand
7
+ ```
8
+
9
+ ---
10
+
11
+ ## Why lite-zustand?
12
+
13
+ Most state management libraries are either too complex or too magical. `lite-zustand` does one thing and does it well: **gives you global state that works everywhere** — inside React components with surgical re-renders, and outside React with direct access and reactive watchers.
14
+
15
+ - **< 1KB** gzipped. No dependencies except React.
16
+ - **Zero unnecessary re-renders.** Components only re-render when selected state actually changes.
17
+ - **Works outside React.** Read, write, and watch state from anywhere — event handlers, timers, utility functions.
18
+ - **Type-safe.** Full TypeScript support with inferred types. No type casting, no `any`.
19
+ - **No boilerplate.** No providers, no context, no reducers, no actions. Just `createStore` and go.
20
+
21
+ ---
22
+
23
+ ## Quick Start
24
+
25
+ ```ts
26
+ import { createStore } from "lite-zustand";
27
+
28
+ const useCounter = createStore({ count: 0, name: "Counter" });
29
+ ```
30
+
31
+ That's it. `useCounter` is now both a **React hook** and a **store accessor**.
32
+
33
+ ---
34
+
35
+ ## API
36
+
37
+ ### `createStore(initialState)`
38
+
39
+ Creates a new store and returns a function that acts as both a React hook and a store namespace.
40
+
41
+ ```ts
42
+ const useStore = createStore({ count: 0, user: "Lider" });
43
+ ```
44
+
45
+ **Returns:** A callable function with `.get()`, `.set()`, `.subscribe()`, and `.watch()` methods attached.
46
+
47
+ ---
48
+
49
+ ### `useStore(selector)`
50
+
51
+ Use inside React components. Subscribes to a slice of state via a selector function.
52
+
53
+ ```tsx
54
+ const [value, set] = useStore((state) => state.count);
55
+ ```
56
+
57
+ | Parameter | Type | Description |
58
+ | ---------- | ------------------ | --------------------------------- |
59
+ | `selector` | `(state: T) => U` | Picks the slice of state to track |
60
+
61
+ **Returns:** `[selectedValue, set]` — a tuple, just like `useState`.
62
+
63
+ > Under the hood, it uses React's `useSyncExternalStore` for concurrent mode safe subscriptions. The selector determines which part of the state your component subscribes to. When state changes, React compares the selected value — if it hasn't changed, no re-render happens.
64
+
65
+ ---
66
+
67
+ ### `.get()`
68
+
69
+ Returns the current state. Works anywhere.
70
+
71
+ ```ts
72
+ const currentState = useStore.get();
73
+ ```
74
+
75
+ ---
76
+
77
+ ### `.set(partial)`
78
+
79
+ Updates the state. Works anywhere. Accepts a partial object or an updater function.
80
+
81
+ ```ts
82
+ // Partial update
83
+ useStore.set({ count: 5 });
84
+
85
+ // Updater function
86
+ useStore.set((state) => ({ count: state.count + 1 }));
87
+ ```
88
+
89
+ State is **shallowly merged**. If you set `{ count: 5 }`, other fields remain untouched.
90
+
91
+ ---
92
+
93
+ ### `.subscribe(listener)`
94
+
95
+ Registers a listener that fires on every state change. Returns an unsubscribe function.
96
+
97
+ ```ts
98
+ const unsubscribe = useStore.subscribe(() => {
99
+ console.log("State changed:", useStore.get());
100
+ });
101
+
102
+ // Later...
103
+ unsubscribe();
104
+ ```
105
+
106
+ ---
107
+
108
+ ### `.watch(selector, callback?)`
109
+
110
+ Watches a specific slice of state. The callback is **optional**.
111
+
112
+ **With callback** — fires only when the selected value changes. Gives you both the new and previous value.
113
+
114
+ ```ts
115
+ useStore.watch(
116
+ (state) => state.count,
117
+ (next, prev) => {
118
+ console.log(`count changed: ${prev} → ${next}`);
119
+ }
120
+ );
121
+ ```
122
+
123
+ This is different from `subscribe` — it only fires when the **selected value** changes, not on every state update.
124
+
125
+ **Without callback** — returns the current selected value as a one-time read. Useful for quick access outside React without needing `.get()` and manually picking a field.
126
+
127
+ ```ts
128
+ const count = useStore.watch((state) => state.count);
129
+ console.log(count); // 0
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Full Example
135
+
136
+ A todo app that demonstrates every feature — store creation, component subscriptions with selectors, state updates, derived values, and reactive watchers outside React.
137
+
138
+ ```ts
139
+ import { createStore } from "lite-zustand";
140
+
141
+ // Create store
142
+ const useTodos = createStore({
143
+ items: [] as { id: number; text: string; done: boolean }[],
144
+ filter: "all" as "all" | "active" | "done",
145
+ });
146
+ ```
147
+
148
+ ```tsx
149
+ // Each component only re-renders when its selected slice changes.
150
+ // FilterBar doesn't care about items, TodoList doesn't care about filter.
151
+
152
+ function FilterBar() {
153
+ const [filter, set] = useTodos((state) => state.filter);
154
+
155
+ return (
156
+ <div>
157
+ <button onClick={() => set({ filter: "all" })}>All</button>
158
+ <button onClick={() => set({ filter: "active" })}>Active</button>
159
+ <button onClick={() => set({ filter: "done" })}>Done</button>
160
+ <p>Current: {filter}</p>
161
+ </div>
162
+ );
163
+ }
164
+
165
+ function TodoCount() {
166
+ // Derived selector — only re-renders when the count actually changes
167
+ const [doneCount] = useTodos(
168
+ (state) => state.items.filter((t) => t.done).length
169
+ );
170
+
171
+ return <p>{doneCount} done</p>;
172
+ }
173
+
174
+ function TodoList() {
175
+ const [items, set] = useTodos((state) => state.items);
176
+
177
+ const addTodo = (text: string) => {
178
+ set((state) => ({
179
+ items: [...state.items, { id: Date.now(), text, done: false }],
180
+ }));
181
+ };
182
+
183
+ const toggleTodo = (id: number) => {
184
+ set((state) => ({
185
+ items: state.items.map((t) =>
186
+ t.id === id ? { ...t, done: !t.done } : t
187
+ ),
188
+ }));
189
+ };
190
+
191
+ return (
192
+ <div>
193
+ <button onClick={() => addTodo("New task")}>Add</button>
194
+ {items.map((item) => (
195
+ <div key={item.id} onClick={() => toggleTodo(item.id)}>
196
+ {item.done ? "✅" : "⬜"} {item.text}
197
+ </div>
198
+ ))}
199
+ </div>
200
+ );
201
+ }
202
+ ```
203
+
204
+ ```ts
205
+ // Outside React — no hooks needed
206
+
207
+ // Read state
208
+ const currentItems = useTodos.get().items;
209
+
210
+ // Quick read with watch (no callback)
211
+ const currentFilter = useTodos.watch((state) => state.filter);
212
+
213
+ // Write state from anywhere (API response, timer, event handler...)
214
+ useTodos.set((state) => ({
215
+ items: [...state.items, { id: Date.now(), text: "From outside", done: false }],
216
+ }));
217
+
218
+ // React to changes — analytics, logging, persistence, side effects
219
+ useTodos.watch(
220
+ (state) => state.items.length,
221
+ (next, prev) => {
222
+ console.log(`Todo count: ${prev} → ${next}`);
223
+ }
224
+ );
225
+
226
+ // Subscribe to all changes
227
+ const unsubscribe = useTodos.subscribe(() => {
228
+ localStorage.setItem("todos", JSON.stringify(useTodos.get()));
229
+ });
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Requirements
235
+
236
+ - React **18+** (uses `useSyncExternalStore`)
237
+ - TypeScript **4.7+** (recommended, not required)
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MIT
@@ -0,0 +1,8 @@
1
+ declare function createStore<T>(initialState: T): (<U>(selector: (state: T) => U) => [U, (partial: Partial<T> | ((state: T) => Partial<T>)) => void]) & {
2
+ get: () => T;
3
+ set: (partial: Partial<T> | ((state: T) => Partial<T>)) => void;
4
+ subscribe: (listener: () => void) => () => void;
5
+ watch: <U>(selector: (state: T) => U, callback?: (selectedState: U, previousState: U) => void) => U | undefined;
6
+ };
7
+
8
+ export { createStore };
@@ -0,0 +1,8 @@
1
+ declare function createStore<T>(initialState: T): (<U>(selector: (state: T) => U) => [U, (partial: Partial<T> | ((state: T) => Partial<T>)) => void]) & {
2
+ get: () => T;
3
+ set: (partial: Partial<T> | ((state: T) => Partial<T>)) => void;
4
+ subscribe: (listener: () => void) => () => void;
5
+ watch: <U>(selector: (state: T) => U, callback?: (selectedState: U, previousState: U) => void) => U | undefined;
6
+ };
7
+
8
+ export { createStore };
package/dist/index.js ADDED
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createStore: () => createStore
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var import_react = require("react");
27
+ function createStore(initialState) {
28
+ let state = initialState;
29
+ const subscribers = /* @__PURE__ */ new Set();
30
+ const get = () => state;
31
+ const set = (partial) => {
32
+ const nextState = typeof partial === "function" ? partial(state) : partial;
33
+ if (!Object.is(nextState, state)) {
34
+ state = typeof nextState !== "object" || nextState === null ? nextState : Object.assign({}, state, nextState);
35
+ subscribers.forEach((listener) => listener());
36
+ }
37
+ };
38
+ const subscribe = (listener) => {
39
+ subscribers.add(listener);
40
+ return () => {
41
+ subscribers.delete(listener);
42
+ };
43
+ };
44
+ const watch = (selector, callback) => {
45
+ let previousValue = selector(state);
46
+ if (callback) {
47
+ subscribe(() => {
48
+ const nextValue = selector(state);
49
+ if (!Object.is(previousValue, nextValue)) {
50
+ const prev = previousValue;
51
+ previousValue = nextValue;
52
+ callback?.(nextValue, prev);
53
+ }
54
+ });
55
+ } else {
56
+ return previousValue;
57
+ }
58
+ };
59
+ const store = (selector) => {
60
+ const value = (0, import_react.useSyncExternalStore)(subscribe, () => selector(get()));
61
+ return [value, set];
62
+ };
63
+ return Object.assign(store, { get, set, subscribe, watch });
64
+ }
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ createStore
68
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,43 @@
1
+ // src/index.ts
2
+ import { useSyncExternalStore } from "react";
3
+ function createStore(initialState) {
4
+ let state = initialState;
5
+ const subscribers = /* @__PURE__ */ new Set();
6
+ const get = () => state;
7
+ const set = (partial) => {
8
+ const nextState = typeof partial === "function" ? partial(state) : partial;
9
+ if (!Object.is(nextState, state)) {
10
+ state = typeof nextState !== "object" || nextState === null ? nextState : Object.assign({}, state, nextState);
11
+ subscribers.forEach((listener) => listener());
12
+ }
13
+ };
14
+ const subscribe = (listener) => {
15
+ subscribers.add(listener);
16
+ return () => {
17
+ subscribers.delete(listener);
18
+ };
19
+ };
20
+ const watch = (selector, callback) => {
21
+ let previousValue = selector(state);
22
+ if (callback) {
23
+ subscribe(() => {
24
+ const nextValue = selector(state);
25
+ if (!Object.is(previousValue, nextValue)) {
26
+ const prev = previousValue;
27
+ previousValue = nextValue;
28
+ callback?.(nextValue, prev);
29
+ }
30
+ });
31
+ } else {
32
+ return previousValue;
33
+ }
34
+ };
35
+ const store = (selector) => {
36
+ const value = useSyncExternalStore(subscribe, () => selector(get()));
37
+ return [value, set];
38
+ };
39
+ return Object.assign(store, { get, set, subscribe, watch });
40
+ }
41
+ export {
42
+ createStore
43
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "lite-zustand",
3
+ "version": "0.0.1",
4
+ "description": "A tiny, type-safe React state manager built on useSyncExternalStore. Granular re-renders via selectors, reactive watchers with previous/next diffing, and direct get/set access outside components — no providers, no boilerplate, no overhead.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "repository": {
9
+ "url": "https://github.com/liderbektas/lite-zustand"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.mjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "peerDependencies": {
23
+ "react": ">=18.0.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "react",
31
+ "react store",
32
+ "state management",
33
+ "zustand",
34
+ "redux",
35
+ "global state",
36
+ "useSyncExternalStore",
37
+ "lightweight state",
38
+ "external store",
39
+ "react state",
40
+ "selector",
41
+ "subscribe",
42
+ "watch",
43
+ "typescript",
44
+ "reactive"
45
+ ],
46
+ "author": "liderbektas",
47
+ "license": "MIT",
48
+ "devDependencies": {
49
+ "@types/react": "^19.2.14",
50
+ "tsup": "^8.5.1",
51
+ "typescript": "^5.9.3"
52
+ }
53
+ }