Reselect: memoized selectors for Redux and plain immutable data
Selector library for Redux
At a glance
- What is it?
- Reselect is the selector library that ships inside Redux Toolkit. It memoizes derived data, keeps result references stable for React-Redux, and defaults to weakMapMemoize in v5.
- Who is it for?
- Adopt Reselect when derived data is recomputed on every render or every store update and the cost is visible. Skip it when the derivation is trivial, or when your data is mutated in place, because reference equality is the trigger for recomputation.
- 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 1 day 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 September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Reselect solves: derived data that recomputes too often
A Redux store is supposed to hold minimal state. Everything else, filtered lists, totals, joined records, is derived. The naive way to derive is a plain function that takes the state and returns a computed value. The README's own example makes the cost visible: a function named selectCompletedTodos logs "selector ran" on every call, and calling it three times with the same state prints that line three times. It also returns a new array each time, so two calls with identical input produce values that are not equal by reference.
That second property is the one that hurts in a UI. Libraries such as React-Redux and React rely on reference equality checks to decide whether to re-render. A selector that returns a fresh array on every call defeats that check even when the underlying data has not moved. Reselect addresses both problems at once: it skips the recomputation, and when it skips, it returns the previous result reference.
The audience is anyone deriving values from an immutable store, which in practice means Redux users first, but the README is explicit that the library works with any plain JS immutable data. If your state is a plain object you replace rather than mutate, Reselect does not care where it came from.
How createSelector wires input selectors to a result function
The API is a single factory with a fixed shape. createSelector takes an array of input selectors, also called dependencies, and a result function. The input selectors receive all the arguments passed to the output selector and extract the pieces the computation needs. The result function receives those extracted values as its arguments and returns the derived value. The README gives the canonical sketch:
const outputSelector = createSelector(
[inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`.
resultFunc // Result function
)On each call, the output selector runs the input selectors, compares their returned values against the previous call, and only invokes the result function when something changed. When nothing changed, it returns the cached result. In the README's completed-todos example, the memoized version prints its log line once across three calls, and two successive calls return the same reference, while the unmemoized version returns two arrays that fail an equality check.
Selectors compose: an output selector is itself a function of the state, so it can be passed as an input selector to another createSelector call. That is what makes the pattern scale to nested derivations, and it is also where the TypeScript cost shows up.
Version 5.0.0 changed the internals of this comparison. The default memoization function became weakMapMemoize, and the output selector now exposes memoize and argsMemoize fields for debugging. The release notes also mention dependencyRecomputations and resetDependencyRecomputations as new output selector fields, which give visibility into how often the input selectors ran.
Installing Reselect and writing a first memoized selector
If you already use Redux Toolkit, stop before installing anything. The README states that Reselect is included by default in the official Redux Toolkit package, so the import comes from there and no separate dependency is needed:
import { createSelector } from '@reduxjs/toolkit'For standalone use, install the reselect package. The README lists three package managers:
# NPM
npm install reselect
# Yarn
yarn add reselect
# pnpm
pnpm add reselectThen build a selector from your state shape. The README's example types the root state, defines a plain selector that filters completed todos, and wraps it with createSelector so the filter only runs when state.todos changes:
import { createSelector } from 'reselect'
interface RootState {
todos: { id: number; completed: boolean }[]
}
const selectCompletedTodos = createSelector(
[(state: RootState) => state.todos],
todos => todos.filter(todo => todo.completed === true)
)What you should see: the result function logs once on the first call, and subsequent calls with the same todos array return the cached array rather than running the filter again. If that log line keeps firing on every call, the todos array reference is changing, which usually means something upstream is producing a new array on each update rather than reusing the old one.
Where the memoization contract breaks
Reselect's cache is keyed on the values the input selectors return, and the check is reference equality, not deep equality. If your reducer mutates an array in place and returns the same reference, the selector will not recompute, and you will serve stale derived data. This is the failure mode that surprises people: the selector looks correct, the store looks correct, and the UI shows the previous value. Immutable updates are a precondition, not a suggestion.
The default memoizer changed in v5, and that matters for edge cases. weakMapMemoize is the new default, while lruMemoize remains available as a documented alternative. The two have different cache behaviour, and code that relied on the previous default's cache size characteristics may behave differently after upgrading. The README does not enumerate the differences; the memoizer API pages are where that comparison lives.
TypeScript users hit a second boundary. Version 5.0.0 discontinued support for TypeScript versions below 4.7, so an older compiler will not work with the current types. The release notes frame the upside: nesting output selectors used to hit a type-instantiation depth limit at roughly 8 selectors, and v5 raised that to around 30, which reduces how often you see the "Type instantiation is excessively deep and possibly infinite" error. That is a real improvement, but it is a limit, not a removal.
Finally, Reselect is the wrong tool when the derivation is cheap. Wrapping a two-property lookup in createSelector adds a layer of indirection and a cache to reason about, and the memoization overhead can exceed the work it saves. The README positions selectors as a way to keep the store minimal and to avoid unnecessary recalculations; it does not claim every derived value needs one.
Reselect compared with plain functions and with React hooks
The honest alternative is no library at all: write a plain function that takes state and returns the derived value, and accept that it runs on every call. That is exactly the unmemoized selectCompletedTodos in the README, and for a small list it is fine. The difference is not correctness, it is reference stability and repeated work. A plain function returns a new array each time, so any consumer doing a reference check re-renders; a memoized selector returns the previous reference when the inputs have not changed. If nothing downstream compares references and the computation is trivial, the plain function wins on simplicity.
A second comparison worth drawing is with Redux Toolkit's own re-export. Since @reduxjs/toolkit ships createSelector, a project already on Toolkit is not choosing between Reselect and something else; it is choosing whether to import from the toolkit or add reselect as a direct dependency. Importing from the toolkit keeps the dependency graph smaller and the versions aligned. Installing reselect directly makes the dependency explicit and lets you track its releases, which matters if you want the standalone package's own versioning rather than whatever the toolkit pins.
The README also points to the Redux docs page on deriving data with selectors, which covers why memoized selectors are useful, typical usage patterns, and how selectors interact with React-Redux. That page, not this library's README, is where the React integration story is spelled out.
Maintenance, releases and upgrade cost
The repository is not archived, and the last push was on 2026-09-19. The most recent release listed is v5.3.0 on 2026-08-22, following v5.2.0 on 2026-05-15. Before that, v5.1.1 landed on 2024-06-01, so the cadence is uneven rather than steady: a long quiet stretch, then two releases within a few months. That pattern is worth knowing if you plan around upstream fixes.
The upgrade cost concentrates in the 4.x to 5.x jump. The release notes list the changes that can affect an existing codebase: the default memoizer became weakMapMemoize, the second overload of createStructuredSelector was removed because it was prone to runtime errors, and TypeScript support below 4.7 was dropped. The memoize and argsMemoize fields were added to the output selector, and the new dependencyRecomputations and resetDependencyRecomputations fields extend what you can inspect. If you never passed a custom memoizer and never used the removed overload, the migration surface is small. If you did, the memoizer pages are the place to check behaviour.
On licensing: the package is MIT. That permits use, modification and redistribution provided the copyright notice and permission notice are included, and it comes with no warranty. Bundling it into a closed-source application is consistent with the licence text. This is a description of the licence, not legal advice; a lawyer should review anything that depends on the outcome.
Editorial conclusion
Adopt Reselect when derived data is recomputed on every render or every store update and the cost is visible. Skip it when the derivation is trivial, or when your data is mutated in place, because reference equality is the trigger for recomputation. Before wiring it in, check which memoizer you are getting: @reduxjs/toolkit re-exports createSelector, while the standalone reselect package defaults to weakMapMemoize in v5, and lruMemoize is the documented alternative. Also confirm your TypeScript version, since v5 dropped support below 4.7. The package is MIT licensed, so redistributing it inside a bundled application is permitted; that is a statement about the licence text, not legal advice.
Frequently asked questions
What is Reselect?
Reselect is a library for creating memoized selector functions, commonly used with Redux but usable with any plain JS immutable data. A selector is not recomputed unless one of its arguments changes, and when it is not recomputed it returns the previous result reference.
What is a Redux selector?
A selector function accepts one or more JavaScript values as arguments and derives a result; when used with Redux, the first argument is typically the entire Redux store state. Reselect's createSelector wraps input selectors and a result function into a memoized output selector.
Do I need to install Reselect separately if I use Redux Toolkit?
No. The README states that Reselect is already included by default in the official Redux Toolkit package, and shows importing createSelector from '@reduxjs/toolkit'. Standalone usage requires installing the reselect package with npm, yarn or pnpm.
Why does my Reselect selector keep recomputing?
The cache compares the values returned by the input selectors by reference. If an upstream update mutates data in place or produces a new array every time, the comparison fails and the result function runs again. Immutable updates are what make the memoization effective.
What changed in Reselect 5.0.0?
The release notes list weakMapMemoize as the new default memoization function, an options object for createSelectorCreator, memoize and argsMemoize fields on the output selector, removal of the second overload of createStructuredSelector, and dropped support for TypeScript below 4.7.
Can Reselect be used without Redux?
Yes. The README describes it as commonly used with Redux but usable with any plain JS immutable data, and the createSelector example itself is written against a plain typed state object.
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/reduxjs-reselect)