@liderbektas/lite-context
v0.1.3
Published
A lightweight, type-safe state management solution for React. Built on Context and useSyncExternalStore, it delivers selective re-rendering with zero dependencies — all under 1KB.
Maintainers
Readme
@liderbektas/lite-context
A lightweight, type-safe state management solution for React.
Built on Context + useSyncExternalStore — selective re-rendering with zero dependencies, all under 1KB.
The Problem
React Context re-renders every consumer when state changes — even if the value they care about hasn't changed. In a real app, this means updating a single field can trigger re-renders across dozens of unrelated components.
@liderbektas/lite-context fixes this. Components only re-render when the specific slice they subscribe to changes.
Installation
npm install @liderbektas/lite-contextRequires React 18+
Examples
Object Store
import createLiteContext from "@liderbektas/lite-context";
interface AuthState {
user: string | null;
role: "admin" | "viewer";
lastLogin: Date | null;
}
const { Provider: AuthProvider, useStore: useAuth } =
createLiteContext<AuthState>({
user: null,
role: "viewer",
lastLogin: null,
});
// Only re-renders when `user` changes — ignores role and lastLogin updates
function Greeting() {
const [user] = useAuth((s) => s.user);
return <h1>Welcome, {user ?? "Guest"}</h1>;
}
// Write-only — never re-renders from store changes
function LoginButton() {
const [, setAuth] = useAuth(() => null);
return (
<button
onClick={() =>
setAuth((s) => ({ ...s, user: "Jane", lastLogin: new Date() }))
}
>
Sign in
</button>
);
}
function App() {
return (
<AuthProvider>
<Greeting />
<LoginButton />
</AuthProvider>
);
}Array Store
import createLiteContext from "@liderbektas/lite-context";
interface Todo {
id: number;
text: string;
done: boolean;
}
const { Provider: TodoProvider, useStore: useTodos } = createLiteContext<Todo[]>([
{ id: 1, text: "Ship lite-context", done: true },
{ id: 2, text: "Write docs", done: false },
{ id: 3, text: "Celebrate", done: false },
]);
// Only re-renders when THIS specific todo changes
function TodoItem({ id }: { id: number }) {
const [todo] = useTodos((s) => s.find((t) => t.id === id));
const [, setTodos] = useTodos(() => null);
if (!todo) return null;
return (
<label style={{ textDecoration: todo.done ? "line-through" : "none" }}>
<input
type="checkbox"
checked={todo.done}
onChange={() =>
setTodos((prev) =>
prev.map((t) => (t.id === id ? { ...t, done: !t.done } : t))
)
}
/>
{todo.text}
</label>
);
}
function App() {
return (
<TodoProvider>
{[1, 2, 3].map((id) => (
<TodoItem key={id} id={id} />
))}
</TodoProvider>
);
}Toggling todo #2 only re-renders that item — the rest stay untouched.
API
createLiteContext<T>(initialState: T)
Returns { Provider, useStore }.
useStore(selector)
const [value, setState] = useStore((store) => store.someField);| Return | Description |
| ---------- | --------------------------------------------------------------------- |
| value | Selected slice of state. Component re-renders only when this changes. |
| setState | Updater function. Receives current state, must return the next state. |
Write-only pattern — pass () => null as selector to get a setter without subscribing to any changes.
Comparison
| Feature | @liderbektas/lite-context | React Context | Zustand | Jotai | | ------------------- | ------------------------- | ------------- | ------- | -------- | | Bundle size | ~1KB | 0 (built-in) | ~1.5KB | ~2.5KB | | Selective re-render | ✅ | ❌ | ✅ | ✅ | | Zero dependencies | ✅ | ✅ | ✅ | ✅ | | TypeScript | ✅ | ✅ | ✅ | ✅ | | Devtools | ❌ | ❌ | ✅ | ✅ | | Middleware | ❌ | ❌ | ✅ | ✅ | | Context-based | ✅ | ✅ | ❌ | ❌ | | Learning curve | Minimal | Minimal | Low | Moderate |
