use-context-selector: a userland useContextSelector for React Context
React useContextSelector hook in userland
At a glance
- What is it?
- use-context-selector emulates the proposed useContextSelector API so a context consumer re-renders only when its selected slice changes. It is a small library with real constraints, and its own README points elsewhere for teams that only want fewer re-renders.
- Who is it for?
- Adopt use-context-selector when you specifically want to emulate the proposed useContextSelector API on top of React Context and you accept the constraints: memoized or externally created provider children, consumers that use the library's hooks rather than React.useContext, and no class components. Do not adopt it merely to cut re-renders; the README itself recommends Zustand, a naive useSyncExternalStore implementation, or experimental react18-use for that goal.
- 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?
- Yes. The repository last received commits 123 days 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 4, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The re-render problem use-context-selector targets
React Context exists to avoid prop drilling, but the README states the known cost plainly: when a context value changes, every component that calls useContext re-renders. If a provider holds a state object with ten fields and one component only reads one field, that component still re-renders when any other field changes. The upstream answer was a proposal called useContextSelector, later folded into a Speculative Mode RFC with context selector support. Neither landed as a stable public API, so this package implements the API in userland.
The audience is narrow and worth naming. This is for React application developers who already use React Context for shared state and want a selector-based read on top of it, and for library authors who want to expose a context whose consumers subscribe to slices. It is not a state management library. There is no store, no actions, no middleware. It is a context primitive with a selector argument, and that is the whole surface.
How the selector mechanism works, and why children must be memoized
The exported createContext returns a context object that useContextSelector understands. Consumers call useContextSelector(context, selector); the hook returns the selected value and re-renders the component only when that selected value is referentially changed. The README warns that the selector should return a referentially equal result for the same input, which in practice means selecting primitives or stable references rather than building a fresh object on each call.
The propagation story is where the design shows its seams. To stop updates from reaching unrelated consumers, the children of a context provider must either be created outside the provider or memoized with React.memo. Skip that and the optimization collapses. The provider also re-renders only when the context value is referentially changed, so an in-place mutation of a state object will not notify anyone. These are not incidental caveats; they are the conditions under which the library works at all.
The README also documents that the implementation deliberately uses a useReducer cheat mode to behave like original React context, and that prior to v1.3 it relied on the undocumented changedBits=0 feature to stop propagation. Version 1.3 dropped that dependency. For concurrent rendering, useContextUpdate wraps an updating function so a value change is applied correctly; the README calls its usage optional and needed only when the default behavior is unexpected. Neither context consumers nor class components are supported.
Installing use-context-selector and building a first selector
The package declares react and scheduler as peer dependencies, so the README tells you to install them yourself rather than relying on transitive resolution:
npm install use-context-selector react schedulerAfter that, create a context with the library's own createContext, not React's. The README's example for a person context looks like this:
import { createContext } from 'use-context-selector';
const PersonContext = createContext({ firstName: '', familyName: '' });Reading a single field is then a selector call. The hook only accepts contexts created by this library's createContext, so a React.createContext object passed here will not work:
import { useContextSelector } from 'use-context-selector';
const firstName = useContextSelector(PersonContext, (state) => state.firstName);The README's counter example shows the shape of a working app: a StateProvider whose value is the tuple returned by useState, and two counter components that each select their own count and the setter separately. Because the two components select different fields, changing count1 does not re-render Counter2. That is the behavior to look for when you run it. The repository ships runnable versions under examples/01_counter, examples/02_person and examples/03_suspense, which you can start with the package scripts examples:01_counter, examples:02_person and examples:03_suspense.
If you only need the whole context value, the README says to use this library's useContext rather than React.useContext, for consistent behavior. For bridging values across multiple React roots, it exposes BridgeProvider and useBridgeValue.
Where use-context-selector is the wrong choice
The README opens with a warning that undercuts the most common reason people reach for the package. Its stated goal is to emulate the React Context API with Concurrent React. Many users, it says, try to use the library to avoid re-renders without needing to consider Concurrent React, and for that narrower goal it recommends Zustand, a naive implementation with useSyncExternalStore, or the experimental react18-use package instead. If your motivation is purely render counts, the maintainer's own advice is to look elsewhere.
The limitations list is the second filter. Provider children must be created outside the provider or wrapped in React.memo, which constrains how you compose a tree and is easy to get wrong silently. Class components and plain context consumers are unsupported. On React 17 and below, the stale props issue exists, and the README notes it can be resolved with unstable_batchedUpdates. Tearing is avoided only if every consumer reads through useContextSelector; if you pass the same data both as props and through this library, the README states they may provide inconsistent values. That last point is the sharpest one: partial adoption is a correctness risk, not just a missed optimization.
use-context-selector versus Zustand and useSyncExternalStore
The difference from Zustand is architectural, not cosmetic. Zustand keeps state in a store outside the React tree, and components subscribe to that store with a selector. There is no provider whose children need memoizing, and no requirement that every consumer use the same hook. use-context-selector keeps state inside React Context, so the provider hierarchy stays the source of truth and the value flows through the tree as context normally does. If your state already lives in a context provider and you want to keep it there, that is the case for this library. If you are choosing a state container from scratch, Zustand avoids the memoization constraint entirely.
The naive useSyncExternalStore route, linked from the README's own issue thread, is the minimal version: an external store plus React's built-in subscription hook, no extra dependency. It gives you selector-driven re-renders without emulating the proposed context API, but it also means your state sits outside Context, which changes how you share it with consumers. The experimental react18-use package is the third option the README names, and the word experimental is the maintainer's, not a judgement added here.
Maintenance, upgrade cost and the MIT licence
The repository is not archived, and the last push was on 2026-06-08. The package.json lists version 2.0.0, and the README documents a breaking point in the 1.x line: prior to v1.3 the implementation depended on the undocumented changedBits=0 feature, and v1.3 removed that dependency. That history matters when you upgrade an old installation, because the mechanism underneath changed even though the public API did not.
Upgrade cost is mostly about peer dependencies. The README explicitly asks library authors to keep peerDependencies declared and to tell users to install react and scheduler themselves. The package is published as type: module with a CommonJS build under dist/cjs and an ESM build under dist/index.js, and it is marked sideEffects: false. The repository pins packageManager [email protected] and its scripts cover formatting, linting, type checks, spec tests via vitest, and API doc generation. The licence is MIT, which is permissive; that is a statement about the licence identifier, not legal advice about your situation.
What to check before you depend on it
Start with the children rule. Open your provider components and confirm that the subtree is either created outside the provider or wrapped in React.memo. If it is not, the propagation stop does not happen and the library's main benefit is absent while its constraints remain.
Next, audit for mixed reads. Search your codebase for places where the same data reaches a component through props and through use-context-selector. The README states that this combination may provide inconsistent values, so any such site is a candidate for tearing rather than a style question. Then check your React version: on React 17 and below the stale props issue applies, and the README points to unstable_batchedUpdates as the resolution. Finally, confirm that no consumer relies on React.useContext or on class components for the same context, since neither is supported.
Editorial conclusion
Adopt use-context-selector when you specifically want to emulate the proposed useContextSelector API on top of React Context and you accept the constraints: memoized or externally created provider children, consumers that use the library's hooks rather than React.useContext, and no class components. Do not adopt it merely to cut re-renders; the README itself recommends Zustand, a naive useSyncExternalStore implementation, or experimental react18-use for that goal. Before committing, verify two things in your own tree: that every provider's children are memoized or defined outside the provider, and that nothing reads the same data through both props and use-context-selector, since the README states that mixing them can produce inconsistent values.
Frequently asked questions
What does use-context-selector do?
It provides a useContextSelector hook in userland so a component can read a selected slice of a context value and re-render only when that selected value is referentially changed. The README describes its goal as emulating the React Context API with Concurrent React.
Should I use use-context-selector, or Zustand, or plain React Context?
The README states that if you simply want to avoid re-renders without considering Concurrent React, you should use Zustand, a naive implementation with useSyncExternalStore, or the experimental react18-use package instead. Zustand keeps state in an external store with selector subscriptions, while use-context-selector keeps state in a Context provider whose children must be memoized or created outside the provider.
Can you show a use-context-selector example?
The README's example creates a context with the library's createContext, wraps children in a provider whose value is the tuple from useState, and has each counter component call useContextSelector to select its own count and the setter separately. Runnable versions live under examples/01_counter, examples/02_person and examples/03_suspense.
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/dai-shi-use-context-selector)