Redux Toolkit 2.14.0 was released on 3 November 2026. It’s a feature release that makes dispatching faster, speeds up RTK Query type checking, and adds some small APIs that a lot of React developers have been asking for: cache entry selectors, a DevTools action sanitizer and filter-based entity removal. It also fixes a long list of RTK Query bugs. This guide shows how to use the new features in a React app and what to check when you upgrade.

Upgrade

npm install @reduxjs/toolkit@^2.14.0 react-redux
# or
pnpm add @reduxjs/toolkit@^2.14.0 react-redux

The release also bumps RTK’s immer dependency to ^11.2.9.

1. RTK Query Cache Entry Selectors

A common request (#2995) was a way to list everything RTK Query has cached for an endpoint. 2.14 adds two memoized selectors:

  • api.util.selectEntriesForEndpoint(state, endpointName): all cache entries for one query or infinite-query endpoint
  • api.util.selectAllQueryEntries(state): entries for every endpoint

Each item includes the entry’s fields plus its queryCacheKey, typed for its endpoint. There’s also a new exported QueryCacheEntry<Definition> type.

// api.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export interface Post { id: number; title: string }

export const api = createApi({
  baseQuery: fetchBaseQuery({ baseUrl: "/api" }),
  endpoints: (build) => ({
    getPost: build.query<Post, number>({ query: (id) => `posts/${id}` }),
  }),
});

export const { useGetPostQuery } = api;
// LoadedPostsBadge.tsx
import { useSelector } from "react-redux";
import { api } from "./api";
import type { RootState } from "./store";

export function LoadedPostsBadge() {
  const loadedIds = useSelector((state: RootState) =>
    api.util
      .selectEntriesForEndpoint(state, "getPost")
      .filter((entry) => entry.status === "fulfilled")
      .map((entry) => entry.originalArgs)
  );

  return <span>{loadedIds.length} posts cached</span>;
}

The selectors themselves are memoized, but the .filter().map() above creates a new array on every call. For heavier components, wrap the derived value with createSelector so React doesn’t re-render without a reason.

import { createSelector } from "@reduxjs/toolkit";
import { api } from "./api";
import type { RootState } from "./store";

export const selectLoadedPostIds = createSelector(
  [(state: RootState) => api.util.selectEntriesForEndpoint(state, "getPost")],
  (entries) => entries.filter((e) => e.status === "fulfilled").map((e) => e.originalArgs)
);

2. A DevTools Action Sanitizer for RTK Query

Every RTK Query request uses the same few action types, so Redux DevTools shows a long list of identical api/executeQuery/fulfilled entries. You had to open each one to see which endpoint it belonged to. The new createApiActionSanitizer(api) renames those actions for display only, for example api/executeQuery/fulfilled (getPosts). The dispatched actions themselves aren’t changed.

// store.ts
import { configureStore } from "@reduxjs/toolkit";
import { createApiActionSanitizer } from "@reduxjs/toolkit/query";
import { api } from "./api";

export const store = configureStore({
  reducer: {
    [api.reducerPath]: api.reducer,
  },
  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(api.middleware),
  devTools: {
    actionSanitizer: createApiActionSanitizer(api),
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

If you have more than one API slice, the createApiActionSanitizer docs explain how to combine sanitizers.

3. Entity Adapter: removeMany({ where }) and selectByIds

adapter.removeMany now accepts a { where } object and removes every entity whose fields match all of the given values. Values are compared with ===, and the match object is typed as Partial<T>.

import { createEntityAdapter, createSlice, type PayloadAction } from "@reduxjs/toolkit";

interface Post { id: number; title: string; authorId: number }

const postsAdapter = createEntityAdapter<Post>();

const postsSlice = createSlice({
  name: "posts",
  initialState: postsAdapter.getInitialState(),
  reducers: {
    postsAdded: postsAdapter.addMany,
    authorRemoved(state, action: PayloadAction<number>) {
      postsAdapter.removeMany(state, { where: { authorId: action.payload } });
    },
  },
});

export const { postsAdded, authorRemoved } = postsSlice.actions;
export default postsSlice.reducer;

// New selector: returns entities in the order of the ids you pass, skipping unknown ids
export const { selectByIds } = postsAdapter.getSelectors();

A safety detail worth knowing: a where object with no keys removes nothing. So if a filter built at runtime ends up empty, it can’t wipe your collection by accident. Use removeAll when you really do want to clear everything. removeMany is also faster now, because it filters a plain copy of the ids against a Set instead of reading through the Immer draft proxy.

4. Faster Dispatch, Without Code Changes

The team added a benchmark harness for the dispatch path and removed work RTK was doing for actions it didn’t care about:

  • createAction(...).match checks action.type first.
  • createReducer, and therefore every createSlice reducer, no longer allocates arrays on every dispatch.
  • The listener middleware keeps a cached snapshot of its listeners and skips its loop entirely for action types nobody listens to.
  • The RTK Query root reducer returns the existing state for actions none of its slices handle, and skips rewriting provided tags when they haven’t changed.

The release notes include PR benchmarks on Node 26. The biggest gain is for the listener middleware with 200 listeners and an unrelated action, which went from 108K to 5.34M operations per second. The notes are honest that apps with only a few listeners will see much smaller improvements. RTK Query’s subscription syncing also does less work now, so Redux DevTools no longer receives a full copy of subscriptions on every sync.

5. Type-Level Improvements

  • RTK-owned Draft, WritableDraft and Patch types. Your generated .d.ts files now point at @reduxjs/toolkit instead of immer. That fixes TS2883 build errors in setups where immer isn’t hoisted.
  • Faster Api type checking. On a test app with a ~50-endpoint API, type instantiations dropped 22%. Check time dropped about 25% on TS 6 and about 18% on the TS 7 native preview.
  • ApiRootState replaces RTK Query’s RootState type, which is now deprecated and will be removed in 3.0.
  • dispatch(removeListener(...)) is now typed as returning boolean, and dispatch(clearAllListeners()) as void.

Upgrade Checklist

  • Replace imports of RTK Query’s RootState type with ApiRootState.
  • If you wrote your own “list cached entries” helper, replace it with selectEntriesForEndpoint.
  • Add createApiActionSanitizer to your store’s devTools option.
  • Re-run tests that cover polling and lifecycles. Many RTK Query timing bugs were fixed, which can change behaviour your tests were accidentally relying on.

Takeaway

Redux Toolkit 2.14 is an easy upgrade with real wins. Dispatch is faster and types check faster without any code changes, and cache selectors, the DevTools sanitizer and removeMany({ where }) make everyday code simpler. The team has also published a roadmap to Redux Toolkit 3.0, so now is a good time to clear deprecations like RootState. Read the full v2.14.0 release notes.