Teaful subscribes components to one store property at a time
🍵 Tiny, easy and powerful React state management
At a glance
- What is it?
- Teaful is a React and Preact state library under 1kb that replaces actions, reducers, selectors and providers with a store object and proxy hooks. It is a good fit for fine-grained re-rendering, but the release history is thin: two versions in March 2026 after a three year gap, and the last push was on 2026-04-09.
- Who is it for?
- Adopt teaful when a small application wants property-level re-rendering without writing reducers, selectors or a provider tree, and when you can live inside its peer dependency on react >= 18.0.0. Skip it if your team needs a state library with an active issue flow, a long release runway or support for React versions below 18.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Subscribing a component to one property instead of the whole tree
Teaful takes a different approach to the problem React's built-in tools leave open. The README is blunt about what it removes: you do not need actions, reducers, selectors, connect, providers or anything of that kind. A store is a plain object, and a component asks for one slice of it through a proxy, so the subscription is per property rather than per store. The stated benefit follows directly from that shape. When a store property is updated, only the components that use that property are re-rendered, and components reading other properties are left alone. The package is small enough to make this cheap: the description calls it less than 1kb, and the bundlewatch configuration in the package manifest puts a ceiling of 1 kB on dist/index.js, 1.1 kB on dist/index.modern.js and 1 kB on both dist/index.m.js and dist/index.umd.js. Those budgets are enforced in CI, so a growing dependency would fail the build rather than quietly enlarge every bundle that uses it.
createStore returns four proxies and no provider
There is no context provider to mount and no store to place above your component tree. You call createStore and it hands back the methods you use to consume and update the store. Four of them come back, and the README documents each with its own type. useStore is the proxy hook for reading and updating properties inside components, and each time the value it returns changes the component renders again with the new value. getStore offers the same shape without a subscription, which makes it usable outside components and inside them, and a change to the value does not cause a re-render. setStore is the write helper for modifying a property from outside a component, for example setStore.user.name('Aral') or setStore.cart.price(price => price + 10). withStore is a higher order component with useStore inside it, offered for components that are not functional. The whole surface is four names, and the guide also covers calculated properties, several stores at once, updating several portions while avoiding re-renders elsewhere, and an onAfterUpdate callback that receives the store and the previous store after each property change.
Reading a value without subscribing to it
getStore is the piece that separates this library from a plain React context, and it deserves more attention than the README gives it. A context provider publishes one value to every consumer below it, so a single change re-renders all of them, whether or not they read the part that changed. Teaful sidesteps that by making the subscription a property of the call rather than of the tree, and getStore is the deliberate escape hatch for code that only wants to read. Handlers, effects and event callbacks can pull the current price without ever enrolling the component in a subscription, which keeps a component from re-rendering just because it once read a value. The catch is that a component using getStore will not update when the value changes, because it never subscribed. That is a trade you have to make deliberately per call site, and it is the reason getStore and useStore look identical in the type signature while behaving differently at render time.
Installing teaful and writing the first store
The package installs like any other React dependency.
yarn add teaful
# or
npm install teaful --saveA store is created with createStore, and the call returns the methods you destructure.
import createStore from "teaful";
const { useStore } = createStore();Pass an initial object when the store starts with data, and the same call then also gives you getStore.
const initialStore = {
cart: { price: 0, items: [] },
};
const { useStore, getStore } = createStore(initialStore);The optional second argument is a function that runs after every update, and it receives the current store and the previous one.
const initialStore = {
cart: { price: 0, items: [] },
};
function onAfterUpdate({ store, prevStore }) {
console.log("This callback is executed after an update");
}
const { useStore } = createStore(initialStore, onAfterUpdate);Inside a component, the hook is used as a proxy so the property you ask for is the property you subscribe to, which is what keeps the re-render narrow.
Named exports keep the proxies intact
There is a small but consequential detail in how the store is exported. The guide recommends a named export of the destructured methods.
export const { useStore, getStore, withStore } = createStore({
cart: { price: 0, items: [] },
});Consumers then import what they need.
import { useStore } from '../store'The guide warns against exporting the whole store as a default export, because the README notes that the proxies do not work well with that shape and a named import from such a module fails. The reason is structural: the store is exposed through Proxy objects, and handing a proxy out through a default export is where the proxy identity gets lost. This is a one line convention, but it is the kind of thing that produces confusing errors later if a team adds a barrel file or re-exports the store by hand. The repository keeps working examples in the examples directory, including api-list, counter-with-esm, nextjs-typescript and todo-list, and the test script runs jest over the examples as well as the unit tests, so the export pattern is exercised rather than merely described.
What the package does not cover
Three limits deserve attention before you pick this over a more conventional store. The first is the React floor. The manifest declares react >= 18.0.0 as a peer dependency, and it also declares teaful-devtools >= 0.4.0, which is a dependency on a separate package for the Devtools part of the guide rather than a bundled feature. The second is scope. Teaful is a store with a subscription model, not a data layer: there is no request cache, no persistence layer, no devtools history beyond the companion package, and no middleware concept. If your application needs those, you are assembling them yourself. The third is the size budget itself, which is a feature and a fence at the same time. Every dependency added to the bundle eats into a limit that bundlewatch checks in CI, so a library that must also carry a date parser, an HTTP client or an immutable data structure cannot fit inside one kilobyte. The README also lists a Teaful Devtools section, and since the devtools live in their own package, the debugging story depends on a version match you maintain outside this repository.
Two releases in March 2026 after three years of silence
The maintenance picture deserves a straight reading. The repository is not archived, but the last push was on 2026-04-09, which is more than six months before the current date, so the code itself has not moved since April. The release history explains why. Version 0.11.0 came out on 2022-09-01, and then nothing until 0.12.0 and 0.12.1, both published on 2026-03-22. So the project had a three year gap with no releases at all, followed by two versions in a single day, and then a final push in April. The default branch is master rather than main, and the build runs through microbundle with jest for tests and bundlewatch for the size ceilings. The licence is the MIT License, with a LICENSE file in the repository root, and the manifest confirms it in the license field. For teams that need predictable maintenance, a two release afternoon after three years of silence is a signal to weigh. The library itself is small enough to vendor internally if it ever stops moving, but the React 18 floor and the peer dependency on teaful-devtools are the parts that would need attention in that case.
Editorial conclusion
Adopt teaful when a small application wants property-level re-rendering without writing reducers, selectors or a provider tree, and when you can live inside its peer dependency on react >= 18.0.0. Skip it if your team needs a state library with an active issue flow, a long release runway or support for React versions below 18. Before wiring it into an application, check the bundle budget that bundlewatch enforces on dist/index.js and dist/index.modern.js, and confirm that the teaful-devtools peer dependency at >= 0.4.0 matches the Devtools page you intend to use, since the last push to the repository was on 2026-04-09 and the gap since 0.11.0 in 2022 was three years long.
Frequently asked questions
How do I install teaful and create a store?
Run yarn add teaful or npm install teaful --save, then call createStore, optionally with an initial store object. The call returns the useStore, getStore, setStore and withStore helpers you destructure.
Does teaful need a provider or reducers?
No. The README states that you do not need actions, reducers, selectors, connect or providers. A store is a plain object and components subscribe through the useStore proxy to a single property.
What is the difference between useStore and getStore in teaful?
useStore subscribes the component to the property it reads, so a change re-renders that component. getStore has the same shape without a subscription, so it can be used outside components and a change in the value does not cause a re-render.
What React version does teaful require?
The package manifest declares react >= 18.0.0 as a peer dependency, and it also lists teaful-devtools >= 0.4.0 for the Devtools part of the guide. The bundlewatch configuration caps dist/index.js at 1 kB and dist/index.modern.js at 1.1 kB.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/teafuljs-teaful)Was this useful?