{"owner":"reduxjs","repo":"redux-toolkit","hasSkills":true,"totalSkillsCount":22,"totalTokensCount":22656,"categories":["plugin-manifest","anthropic-skill"],"hasMcp":false,"mcpConfig":null,"found":["packages/rtk-codemods/README.md","packages/rtk-codemods/transforms/createReducerBuilder/README.md","packages/rtk-codemods/transforms/createSliceBuilder/README.md","packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md","packages/rtk-query-codegen-openapi/README.md","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md","packages/toolkit/README.md","packages/toolkit/scripts/issue-triage/README.md","packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md","packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md","packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md","packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md","packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md","packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md","packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md","packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md","packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md"],"skills":{"packages/rtk-codemods/README.md":"# RTK Codemods\n\nA collection of codemods for updating legacy Redux Toolkit API usage patterns to modern patterns.\n\n## Usage\n\nTo run a specific codemod from this project, you would run the following:\n\n```bash\nnpx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Transforms\n\n<!--TRANSFORMS_START-->\n\n- [createReducerBuilder](transforms/createReducerBuilder/README.md)\n- [createSliceBuilder](transforms/createSliceBuilder/README.md)\n- [createSliceReducerBuilder](transforms/createSliceReducerBuilder/README.md)\n\n<!--TRANSFORMS_END-->\n\n## Contributing\n\n### Installation\n\n- clone the repo\n- change into the repo directory\n- `pnpm install`\n\n### Running tests\n\n- `pnpm --filter @reduxjs/rtk-codemods test`\n","packages/rtk-codemods/transforms/createReducerBuilder/README.md":"# createReducerBuilder\n\nRewrites uses of the \"object\" syntax for Redux Toolkit's `createReducer` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\ncreateReducer(initialState, {\n  [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n    // stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded](state: SliceState, action: PayloadAction<string>) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createReducerBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\ncreateReducer(initialState, {\n  [todoAdded1a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded1b]: (state, action) => action.payload,\n  [todoAdded1c + 'test']: (state, action) => {\n    // stuff\n  },\n  [todoAdded1d](state, action) {\n    // stuff\n  },\n  [todoAdded1e]: function (state, action) {\n    // stuff\n  },\n  todoAdded1f: (state, action) => {\n    //stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded2a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded2b](state, action) {\n    // stuff\n  },\n  [todoAdded2c]: function (state, action) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createReducerBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded1a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n  builder.addCase(todoAdded1c + 'test', (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1d, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1e, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1f, (state, action) => {\n    //stuff\n  })\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded2a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2b, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2c, (state, action) => {\n    // stuff\n  })\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-codemods/transforms/createSliceBuilder/README.md":"# createSliceBuilder\n\nRewrites uses of the `extraReducers` \"object\" syntax for Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n  extraReducers: {\n    [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n  extraReducers: {\n    [todoAdded](state: SliceState, action: PayloadAction<string>) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createSliceBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n  extraReducers: {\n    [todoAdded1a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded1b]: (state, action) => action.payload,\n    [todoAdded1c + 'test']: (state, action) => {\n      // stuff\n    },\n    [todoAdded1d](state, action) {\n      // stuff\n    },\n    [todoAdded1e]: function (state, action) {\n      // stuff\n    },\n    todoAdded1f: (state, action) => {\n      //stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n  extraReducers: {\n    [todoAdded2a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded2b](state, action) {\n      // stuff\n    },\n    [todoAdded2c]: function (state, action) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createSliceBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded1a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n    builder.addCase(todoAdded1c + 'test', (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1d, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1e, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1f, (state, action) => {\n      //stuff\n    })\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded2a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2b, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2c, (state, action) => {\n      // stuff\n    })\n  }\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md":"# createSliceReducerBuilder\n\nRewrites uses of Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax for the `reducers` field, to make it easier to add prepared reducers and thunks inside of `createSlice`.\n\nNote that unlike the `createReducerBuilder` and `createSliceBuilder` transforms (which both were fixes for deprecated/removed overloads), this is entirely optional. You do not _need_ to apply this to an entire codebase unless you specifically want to. Otherwise, feel free to apply to specific slice files as needed.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```\nnpx @reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\n@reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n<!--FIXTURES_TOC_END-->\n\n<!--FIXTURES_CONTENT_START-->\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-query-codegen-openapi/README.md":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/rtk-incubator/rtk-query/main/logo.png\" width=\"400\" />\n</p>\n<h2 align=\"center\">\nCode Generator\n</h2>\n\n<p align=\"center\">\n   <a href=\"https://discord.gg/0ZcbPKXt5bZ6au5t\" target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/chat-online-green\" alt=\"Discord server\" />\n  </a>\n</p>\n\n### Introduction\n\nThis is a utility library meant to be used with [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) that will generate a typed API client from an OpenAPI schema.\n\n### Documentation\n\n[View the RTK Query Code Generation docs](https://redux-toolkit.js.org/rtk-query/usage/code-generation)\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md":"---\nname: manage-server-data/generate-rtk-query-from-openapi\ndescription: >\n  Use this when generating RTK Query endpoints from OpenAPI schemas with\n  @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints,\n  endpointOverrides, generated tags, and reviewing generated output before it\n  becomes part of the app.\ntype: composition\nlibrary: '@rtk-query/codegen-openapi'\nlibrary_version: '2.2.0'\nrequires:\n  - manage-server-data/adopt-rtk-query\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/types.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/generate.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/README.md'\n---\n\n# Generate RTK Query From OpenAPI\n\n## Setup\n\n```ts\n// file: src/store/emptyApi.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n\n// file: openapi-config.ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n};\n\nexport default config;\n```\n\nRun:\n\n```bash\nnpx @rtk-query/codegen-openapi openapi-config.ts\n```\n\n## Core Patterns\n\n### Generate into an empty shared API\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nCodegen works best when it extends a single RTK Query architecture instead of creating standalone API roots.\n\n### Filter endpoints when the schema is too broad\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/userApi.ts',\n  exportName: 'userApi',\n  hooks: true,\n  filterEndpoints: ['loginUser', /User/],\n};\n\nexport default config;\n```\n\nStart with a narrow slice of the schema if the full surface area is too noisy or the package boundaries differ from the OpenAPI file.\n\n### Use `endpointOverrides` to fix generation results\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n    {\n      pattern: /.*/,\n      parameterFilter: (_name, parameter) => parameter.in !== 'header',\n    },\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n\nexport default config;\n```\n\nReview generated endpoints and override type, parameter, or tag behavior instead of hand-editing the emitted file.\n\n## Common Mistakes\n\n### HIGH Assuming generated tags are already specific enough\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n```\n\nGenerated tags are string-only by default, so they can invalidate more cache than intended.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### HIGH Generating a brand-new API root instead of extending an empty one\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\ntype Pet = { id: string; name: string };\n\nexport const petApi = createApi({\n  reducerPath: 'petApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getPetById: build.query<Pet, string>({\n      query: (id) => `pets/${id}`,\n    }),\n  }),\n});\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nGenerated code should plug into one RTK Query architecture so invalidation and store wiring stay coherent.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### MEDIUM Trusting generated shapes without overrides or review\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n  ],\n};\n```\n\nReal schemas often need type, parameter, or tag correction; treat generated output as reviewed source, not gospel.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n## References\n\n- [Codegen override patterns and review checklist](references/codegen-overrides.md)\n","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md":"# Codegen Overrides\n\n## Common override targets\n\n| Need                                            | Override                           |\n| ----------------------------------------------- | ---------------------------------- |\n| Query should really be a mutation               | `type: 'mutation'`                 |\n| Header or optional parameters should be omitted | `parameterFilter`                  |\n| Generated tags are too coarse                   | `providesTags` / `invalidatesTags` |\n\n## Example: split one schema into multiple outputs\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFiles: {\n    './src/store/userApi.ts': {\n      exportName: 'userApi',\n      filterEndpoints: [/user/i],\n      hooks: true,\n    },\n    './src/store/orderApi.ts': {\n      exportName: 'orderApi',\n      filterEndpoints: [/order/i],\n      hooks: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Review checklist\n\n- Does every generated endpoint belong in the shared API root?\n- Did any query or mutation type need correction?\n- Are generated tags specific enough for real invalidation needs?\n- Did the schema force parameters into the signature that the app should hide?\n","packages/toolkit/README.md":"# Redux Toolkit\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-toolkit/tests.yml?style=flat-square)\n[![npm version](https://img.shields.io/npm/v/@reduxjs/toolkit.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/toolkit)\n[![npm downloads](https://img.shields.io/npm/dm/@reduxjs/toolkit.svg?style=flat-square&label=RTK+downloads)](https://www.npmjs.com/package/@reduxjs/toolkit)\n\n**The official, opinionated, batteries-included toolset for efficient Redux development**\n\n## Installation\n\n### Create a React Redux App\n\nThe recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).\n\nBoth of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.\n\n```bash\n# Vite with our Redux+TS template\n# (using the `tiged` tool to clone and extract the template)\nnpx tiged reduxjs/redux-templates/packages/vite-template-redux my-app\n\n# Next.js using the `with-redux` template\nnpx create-next-app --example with-redux my-app\n```\n\nFor React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):\n\n```bash\nnpx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app\n```\n\n### An Existing App\n\nRedux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:\n\n```bash\n# NPM\nnpm install @reduxjs/toolkit\n\n# Yarn\nyarn add @reduxjs/toolkit\n```\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n\nThe package includes a precompiled browser ESM build that can be loaded from a `<script type=\"module\">` tag. No-bundler browser usage also needs an import map, because the build imports Redux Toolkit's dependencies by package name. See the [Getting Started docs](https://redux-toolkit.js.org/introduction/getting-started#an-existing-app) for an example.\n\n## Documentation\n\nThe Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit.\n\nThe Redux core docs at https://redux.js.org includes the full Redux tutorials, as well usage guides on general Redux patterns.\n\n## Purpose\n\nThe **Redux Toolkit** package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:\n\n- \"Configuring a Redux store is too complicated\"\n- \"I have to add a lot of packages to get Redux to do anything useful\"\n- \"Redux requires too much boilerplate code\"\n\nWe can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.\n\nBecause of that, this package is deliberately limited in scope. It does _not_ address concepts like \"reusable encapsulated Redux modules\", folder or file structures, managing entity relationships in the store, and so on.\n\nRedux Toolkit also includes a powerful data fetching and caching capability that we've dubbed \"RTK Query\". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.\n\n## What's Included\n\nRedux Toolkit includes these APIs:\n\n- `configureStore()`: wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, add whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.\n- `createReducer()`: lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/mweststrate/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.\n- `createAction()`: generates an action creator function for the given action type string. The function itself has `toString()` defined, so that it can be used in place of the type constant.\n- `createSlice()`: combines `createReducer()` + `createAction()`. Accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.\n- `combineSlices()`: combines multiple slices into a single reducer, and allows \"lazy loading\" of slices after initialisation.\n- `createListenerMiddleware()`: lets you define \"listener\" entries that contain an \"effect\" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes. A lightweight alternative to Redux async middleware like sagas and observables.\n- `createAsyncThunk()`: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/resolved/rejected` action types based on that promise\n- `createEntityAdapter()`: generates a set of reusable reducers and selectors to manage normalized data in the store\n- The `createSelector()` utility from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.\n\nFor details, see [the Redux Toolkit API Reference section in the docs](https://redux-toolkit.js.org/api/configureStore).\n\n## RTK Query\n\n**RTK Query** is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.\n\nRTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](https://redux.js.org/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.\n\nRTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:\n\n```ts no-transpile\nimport { createApi } from '@reduxjs/toolkit/query'\n\n/* React-specific entry point that automatically generates\n   hooks corresponding to the defined endpoints */\nimport { createApi } from '@reduxjs/toolkit/query/react'\n```\n\n### What's included\n\nRTK Query includes these APIs:\n\n- `createApi()`: The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with \"one API slice per base URL\" as a rule of thumb.\n- `fetchBaseQuery()`: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.\n- `<ApiProvider />`: Can be used as a Provider if you do not already have a Redux store.\n- `setupListeners()`: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.\n\nSee the [**RTK Query Overview**](https://redux-toolkit.js.org/rtk-query/overview) page for more details on what RTK Query is, what problems it solves, and how to use it.\n\n## Contributing\n\nPlease refer to our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Redux Toolkit.\n","packages/toolkit/scripts/issue-triage/README.md":"# GitHub Issues Triage Tool\n\nA TypeScript-based tool for automatically triaging and categorizing GitHub issues in the Redux Toolkit repository.\n\n## Features\n\n- Fetches open issues from GitHub API\n- Categorizes issues based on labels, titles, and content\n- Generates markdown reports with statistics\n- Caches data to avoid API rate limiting\n- Supports both Node.js and Bun runtimes\n\n## Setup\n\n1. Install dependencies:\n\n```bash\nnpm install\n```\n\n2. Build the TypeScript code:\n\n```bash\nnpm run build\n```\n\n## Usage\n\nRun the triage tool:\n\n```bash\nnpm start\n```\n\nOr for development (builds and runs):\n\n```bash\nnpm run dev\n```\n\n### Using Cached Data\n\nThe tool automatically saves fetched data to `cache/issues-data.json` after each run. To use cached data instead of fetching fresh data (useful for development and testing):\n\n```bash\nnpm start -- --use-cache\n```\n\nOr with the dev command:\n\n```bash\nnpm run dev -- --use-cache\n```\n\n**Note:** The `--use-cache` flag is primarily for development/testing purposes. It loads data from the cache file instantly without making API calls. If the cache file doesn't exist or is corrupted, the tool will automatically fetch fresh data.\n\n## Project Structure\n\n```\nscripts/issue-triage/\n├── src/           # TypeScript source files\n├── dist/          # Compiled JavaScript output\n├── cache/         # Cached JSON data from GitHub\n├── reports/       # Generated triage reports\n├── package.json   # Project dependencies\n└── tsconfig.json  # TypeScript configuration\n```\n\n## Requirements\n\n- Node.js 18+ or Bun 1.0+\n- TypeScript 5.0+\n\n## Development\n\nThis tool is built with TypeScript using ES modules (ESM). The configuration supports both Node.js and Bun runtimes for maximum flexibility.\n","packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md":"---\nname: build-modern-redux-apps/modern-redux\ndescription: >\n  Use this when setting up a new Redux Toolkit app or modernizing an existing\n  React + Redux codebase. Covers configureStore, Provider wiring, typed hooks,\n  hooks-first React-Redux usage, feature folders, and the correct store lifetime\n  for SPA and SSR-heavy React environments.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/typescript.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/nextjs.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Modern Redux\n\n## Setup\n\n```tsx\n// file: src/features/counter/counterSlice.ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nexport const counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\nexport const { increment } = counterSlice.actions\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterSlice.reducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\n// file: src/app/hooks.ts\nimport { useDispatch, useSelector } from 'react-redux'\nimport type { AppDispatch, RootState } from './store'\n\nexport const useAppDispatch = useDispatch.withTypes<AppDispatch>()\nexport const useAppSelector = useSelector.withTypes<RootState>()\n\n// file: src/features/counter/Counter.tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n\n// file: src/main.tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { Counter } from './features/counter/Counter'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <Provider store={store}>\n    <Counter />\n  </Provider>,\n)\n```\n\n## Core Patterns\n\n### Keep React components on hooks, not wrappers\n\n```tsx\nimport { postAdded, selectPosts } from './postsSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector(selectPosts)\n  const dispatch = useAppDispatch()\n\n  return (\n    <button\n      onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}\n    >\n      {posts.length}\n    </button>\n  )\n}\n```\n\nHooks are the default React-Redux integration for new code.\n\n### Create the store inside the provider for SSR-heavy React apps\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      counter: counterSlice.reducer,\n    },\n  })\n\nexport type AppStore = ReturnType<typeof makeStore>\nexport type RootState = ReturnType<AppStore['getState']>\nexport type AppDispatch = AppStore['dispatch']\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nIn SSR-heavy React frameworks, create a store per request and keep that instance stable across renders.\n\n### Keep app wiring in `app/` and feature logic in feature folders\n\n```text\nsrc/\n  app/\n    store.ts\n    hooks.ts\n  features/\n    posts/\n      postsSlice.ts\n      PostsList.tsx\n    users/\n      usersSlice.ts\n      UsersList.tsx\n```\n\nThis keeps store wiring centralized and feature logic colocated.\n\n## Common Mistakes\n\n### HIGH Importing the store in React components\n\nWrong:\n\n```tsx\nimport { store } from '../../app/store'\n\nexport function PostsList() {\n  const posts = store.getState().posts\n  return <div>{posts.length}</div>\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts)\n  return <div>{posts.length}</div>\n}\n```\n\nReact components should read through context and hooks; direct store imports are a separate escape hatch for non-React integrations, not the default UI pattern.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Defaulting to `connect()` in new React code\n\nWrong:\n\n```tsx\nimport { connect } from 'react-redux'\n\nconst increment = () => ({ type: 'counter/increment' as const })\nconst mapState = (state: { counter: { value: number } }) => ({\n  value: state.counter.value,\n})\nconst mapDispatch = { increment }\n\nfunction Counter({\n  value,\n  increment,\n}: ReturnType<typeof mapState> & typeof mapDispatch) {\n  return <button onClick={() => increment()}>{value}</button>\n}\n\nexport default connect(mapState, mapDispatch)(Counter)\n```\n\nCorrect:\n\n```tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n```\n\nHooks are the modern default, simpler to type, and the maintainers explicitly want agents to steer new code away from `connect`.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Recreating the store during render in SSR-heavy apps\n\nWrong:\n\n```tsx\n'use client'\n\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const store = makeStore()\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCorrect:\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nA new store on every render loses client state, while a module singleton can leak across requests on the server.\n\nSource: reduxjs/redux-toolkit:docs/usage/nextjs.mdx\n\n### HIGH Keeping `createStore` boilerplate as the default\n\nWrong:\n\n```ts\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nconst rootReducer = combineReducers({\n  counter: counterReducer,\n})\n\nexport const store = createStore(rootReducer, applyMiddleware(thunk))\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer,\n  },\n})\n```\n\nManual store setup works, but it throws away RTK's default middleware, dev checks, and the current recommended baseline.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n## References\n\n- [Store lifetime and framework boundaries](references/store-lifetime.md)\n","packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md":"# Store Lifetime\n\n## Decision table\n\n| Environment                | Default store shape                     | Why                                                                                                  |\n| -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |\n| Client-only SPA            | One module-level singleton store        | There is one browser session and no cross-request leakage risk.                                      |\n| SSR-heavy React app        | `makeStore()` plus provider-local state | Each request needs its own store instance, but that instance must stay stable across client renders. |\n| Non-React integration code | Direct store access can be acceptable   | This is outside the React context boundary and should stay out of UI components.                     |\n\n## SPA pattern\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n```\n\nUse this for classic browser SPAs.\n\n## SSR-heavy React pattern\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      posts: postsSlice.reducer,\n    },\n  })\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCreate the store per request, then keep it stable inside the provider component.\n","packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md":"---\nname: build-modern-redux-apps/redux-dataflow\ndescription: >\n  Use this when you need the Redux event -> reducer -> selector -> render loop,\n  event-style actions, reducer-owned state transitions, derived data, or a\n  debugging model for Redux Toolkit apps.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Redux Dataflow\n\n## Setup\n\n```ts\nimport { configureStore, createSelector, createSlice } from '@reduxjs/toolkit'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as { id: string; title: string; published: boolean }[],\n    filter: 'all' as 'all' | 'published',\n  },\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.items.push({ ...action.payload, published: false })\n    },\n    postPublished(state, action: { payload: { id: string } }) {\n      const post = state.items.find((item) => item.id === action.payload.id)\n      if (post) {\n        post.published = true\n      }\n    },\n    filterChanged(state, action: { payload: 'all' | 'published' }) {\n      state.filter = action.payload\n    },\n  },\n})\n\nconst store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n\nconst selectPostsState = (state: RootState) => state.posts\nconst selectVisiblePosts = createSelector([selectPostsState], (postsState) =>\n  postsState.filter === 'all'\n    ? postsState.items\n    : postsState.items.filter((post) => post.published),\n)\n\nstore.dispatch(postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' }))\nstore.dispatch(postsSlice.actions.postPublished({ id: 'p1' }))\n\nconst visiblePosts = selectVisiblePosts(store.getState())\nconsole.log(visiblePosts)\n```\n\n## Core Patterns\n\n### Dispatch events, not setters\n\n```ts\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string }[],\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.push(action.payload)\n    },\n    postRemoved(state, action: { payload: { id: string } }) {\n      return state.filter((post) => post.id !== action.payload.id)\n    },\n    postUpdated(\n      state,\n      action: { payload: { id: string; changes: Partial<{ title: string }> } },\n    ) {\n      const post = state.find((item) => item.id === action.payload.id)\n      if (post && action.payload.changes.title) {\n        post.title = action.payload.changes.title\n      }\n    },\n  },\n})\n\npostsSlice.actions.postAdded({ id: 'p1', title: 'Draft' })\n```\n\nEvent-style actions explain what happened in the UI instead of hiding the transition behind a generic setter.\n\n### Let reducers combine old store data with new outside data\n\n```ts\nimport { createEntityAdapter, createSlice } from '@reduxjs/toolkit'\n\nconst postsAdapter = createEntityAdapter<{ id: string; title: string }>()\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: postsAdapter.getInitialState(),\n  reducers: {\n    postsReceived(state, action: { payload: { id: string; title: string }[] }) {\n      postsAdapter.upsertMany(state, action.payload)\n    },\n  },\n})\n\nconst incomingPosts = [\n  { id: 'p1', title: 'Draft' },\n  { id: 'p2', title: 'Published' },\n]\n\npostsSlice.actions.postsReceived(incomingPosts)\n```\n\nIf a transition mixes current store state with new external data, dispatch the new external data and let the reducer own the merge.\n\n### Derive values with selectors instead of storing duplicates\n\n```ts\nimport { createSelector } from '@reduxjs/toolkit'\n\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectFilter = (state: RootState) => state.posts.filter\n\nexport const selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nSelectors keep a single source of truth in state while still exposing the shapes the UI needs.\n\n## Common Mistakes\n\n### CRITICAL Mutating selected state outside reducers\n\nWrong:\n\n```ts\nconst post = selectPostById(store.getState(), 'p1')\n\nif (post) {\n  post.title = 'Changed in place'\n}\n```\n\nCorrect:\n\n```ts\nstore.dispatch(\n  postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),\n)\n```\n\nObjects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using setter-style actions instead of event-style actions\n\nWrong:\n\n```ts\nconst nextPosts = [\n  ...selectPosts(store.getState()),\n  { id: 'p2', title: 'Write docs' },\n]\nstore.dispatch(setPosts(nextPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postAdded({ id: 'p2', title: 'Write docs' }))\n```\n\nActions should describe events, not ask reducers to blindly replace state with a precomputed value.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Combining store state before dispatch\n\nWrong:\n\n```ts\nconst currentPosts = selectPosts(store.getState())\nconst mergedPosts = [\n  ...currentPosts.filter(\n    (currentPost) =>\n      !incomingPosts.some((incomingPost) => incomingPost.id === currentPost.id),\n  ),\n  ...incomingPosts,\n]\n\nstore.dispatch(postsReplaced(mergedPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postsReceived(incomingPosts))\n```\n\nIf the next state depends on current store state, the reducer should own that combination logic; only authoritative external snapshots should replace state wholesale.\n\nSource: maintainer interview\n\n### HIGH Ignoring current state in async reducers\n\nWrong:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  state.status = 'succeeded'\n  state.items = action.payload\n})\n```\n\nCorrect:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  if (state.status === 'pending') {\n    state.status = 'succeeded'\n    state.items = action.payload\n  }\n})\n```\n\nReducers that treat every lifecycle action as valid can move the slice into impossible states or let stale requests win.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### MEDIUM Storing derived values in state\n\nWrong:\n\n```ts\nconst initialState = {\n  items: [] as Post[],\n  visiblePosts: [] as Post[],\n}\n```\n\nCorrect:\n\n```ts\nconst selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nDerived values drift out of sync quickly; keep the raw state and derive the view shape.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n","packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md":"---\nname: evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps\ndescription: >\n  Use this when debugging duplicate requests, stale cache behavior, broad\n  subscriptions, selector churn, serializability warnings, or other Redux\n  Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector ->\n  render debugging loop plus RTK Query cache interpretation.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md'\n  - 'reduxjs/redux-toolkit:docs/usage/usage-guide.md'\n---\n\n# Debug Redux Toolkit Apps\n\n## Setup\n\n```ts\nimport { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as Post[],\n    status: 'idle' as 'idle' | 'pending' | 'succeeded' | 'failed',\n  },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchPosts.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(fetchPosts.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.items = action.payload\n      })\n  },\n})\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n```\n\n## Core Patterns\n\n### Debug in order: action -> reducer -> selector -> render\n\n```ts\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectPostsStatus = (state: RootState) => state.posts.status\n\nstore.dispatch(fetchPosts())\n\nconsole.log(selectPostsStatus(store.getState()))\nconsole.log(selectPosts(store.getState()))\n```\n\nIf a component looks wrong, first verify the action fired, then the reducer state, then the selector result, then the render boundary.\n\n### Narrow subscriptions at the usage site\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{posts.length}</div>\n    </div>\n  )\n}\n```\n\nReact-Redux behaves best when components select only the values they render and do it as close to usage as possible.\n\n### Interpret RTK Query invalidation correctly\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf invalidation did not visibly refetch, check whether anything was still subscribed to that cache entry.\n\n## Common Mistakes\n\n### HIGH Dispatching fetch thunks from effects without a thunk-level guard\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const dispatch = useAppDispatch()\n  const postStatus = useAppSelector((state) => state.posts.status)\n\n  useEffect(() => {\n    if (postStatus === 'idle') {\n      dispatch(fetchPosts())\n    }\n  }, [dispatch, postStatus])\n\n  return null\n}\n```\n\nCorrect:\n\n```ts\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n```\n\nReact StrictMode can run effects twice in development, so the guard belongs in the thunk as well as the component.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### HIGH Ignoring serializable-state warnings\n\nWrong:\n\n```ts\nconst initialState = {\n  lastSeen: new Date(),\n  pendingIds: new Set<string>(),\n}\n```\n\nCorrect:\n\n```ts\nconst initialState = {\n  lastSeenIso: new Date().toISOString(),\n  pendingIds: [] as string[],\n}\n```\n\nNon-serializable values break DevTools, replay, persistence, and equality assumptions in subtle ways.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Selecting broad state in parents and threading props\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const postsState = useAppSelector((state) => state.posts)\n  return <PostsList items={postsState.items} status={postsState.status} />\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsList() {\n  const items = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{items.length}</div>\n    </div>\n  )\n}\n```\n\nSelecting whole slices high in the tree widens the subscription surface and pushes rerenders through props.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Returning unstable objects from query selection logic\n\nWrong:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: [...data],\n  }),\n})\n```\n\nCorrect:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: data,\n  }),\n})\n```\n\nNew object and array references defeat memoization and make components rerender even when the underlying cached data did not change.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md\n","packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md":"---\nname: evolve-and-diagnose-redux-apps/migrate-to-modern-redux\ndescription: >\n  Use this when moving a legacy Redux codebase to current RTK patterns. Covers\n  replacing createStore with configureStore, migrating touched reducers to\n  createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks\n  with RTK Query instead of writing new legacy Redux code.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux-toolkit:packages/rtk-codemods/README.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Migrate To Modern Redux\n\n## Setup\n\n```ts\n// before\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nconst rootReducer = combineReducers({\n  posts: postsReducer,\n  users: usersReducer,\n})\n\nexport const legacyStore = createStore(rootReducer, applyMiddleware(thunk))\n\n// after\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n```\n\n## Core Patterns\n\n### Replace the store setup first\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nThis is the one migration step that can happen immediately while old reducers continue to work.\n\n### Migrate reducers as you touch them\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype TodosState = {\n  items: { id: string; text: string; completed: boolean }[]\n}\n\nconst initialState: TodosState = {\n  items: [],\n}\n\nexport const todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.items.push({ ...action.payload, completed: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.items.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.completed = !todo.completed\n      }\n    },\n  },\n})\n```\n\nOnce a reducer needs editing, migrate that reducer instead of adding more legacy code to it.\n\n### Use codemods for mechanical updates\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder src/features/posts/postsSlice.ts\nnpx @reduxjs/rtk-codemods createReducerBuilder src/features/posts/postsReducer.ts\n```\n\nUse codemods for repetitive RTK API migrations, then review the result and finish the semantic cleanup by hand.\n\n### Replace legacy server-data stacks with RTK Query\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nWhen the old code is just request status plus fetched data, migrate toward RTK Query instead of carrying the thunk stack forward forever.\n\n## Common Mistakes\n\n### HIGH Attempting a big-bang rewrite\n\nWrong:\n\n```ts\n// Replace every reducer, every connected component, and every async flow\n// in one branch before shipping anything.\n```\n\nCorrect:\n\n```ts\n// 1) Switch createStore to configureStore\n// 2) Migrate one touched reducer to createSlice\n// 3) Convert touched connected components to hooks\n// 4) Repeat without introducing new legacy Redux code\n```\n\nModern Redux migration is incremental, but once the store is modernized new work should stop adding legacy patterns.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### CRITICAL Carrying removed RTK 2 config forms forward\n\nWrong:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: [logger],\n})\n```\n\nCorrect:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),\n})\n```\n\nRTK 2 removed array middleware configuration and other older builder forms that agents trained on RTK 1.x still emit.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Preserving hand-written fetch state by default\n\nWrong:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\ntype Todo = { id: string; text: string }\n\nconst initialState = {\n  items: [] as Todo[],\n  status: 'idle' as 'idle' | 'pending' | 'failed',\n}\n\nexport const fetchTodos = createAsyncThunk('todos/fetch', async () => {\n  const response = await fetch('/api/todos')\n  return (await response.json()) as Todo[]\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nIf the feature is really server cache, keep the migration moving toward RTK Query instead of rebuilding the old loading-flag architecture in new APIs.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n","packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md":"---\nname: manage-server-data/adopt-rtk-query\ndescription: >\n  Use this when adding RTK Query as the default server-data and document-cache\n  layer. Covers createApi, store integration, hooks, invalidation behavior,\n  optimistic updates, and deciding when RTK Query is the right cache model.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Adopt RTK Query\n\n## Setup\n\n```tsx\n// file: src/services/api.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    addPost: build.mutation<Post, Pick<Post, 'title'>>({\n      query: (body) => ({\n        url: 'posts',\n        method: 'POST',\n        body,\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n\nexport const { useGetPostsQuery, useAddPostMutation } = api\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { api } from '../services/api'\n\nexport const store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n\n// file: src/App.tsx\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { useAddPostMutation, useGetPostsQuery } from './services/api'\n\nfunction Posts() {\n  const { data: posts = [] } = useGetPostsQuery()\n  const [addPost] = useAddPostMutation()\n\n  return (\n    <div>\n      <button onClick={() => addPost({ title: 'Write docs' })}>Add</button>\n      <ul>\n        {posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nexport function App() {\n  return (\n    <Provider store={store}>\n      <Posts />\n    </Provider>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep one API slice per base URL and extend it\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n\nexport const postsApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getPosts: build.query<{ id: string; title: string }[], void>({\n      query: () => 'posts',\n    }),\n  }),\n})\n```\n\nSplit files with `injectEndpoints`, not by making multiple `createApi` roots for the same backend.\n\n### Use tags for cache invalidation\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: (_result, _error, { id }) => [{ type: 'Post', id }],\n    }),\n  }),\n})\n```\n\nTreat tags as the normal invalidation path before reaching for manual cache patching.\n\n### Do optimistic updates in endpoint lifecycles\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n        const patch = dispatch(\n          api.util.updateQueryData('getPosts', undefined, (draft) => {\n            const post = draft.find((item) => item.id === id)\n            if (post) {\n              post.title = title\n            }\n          }),\n        )\n\n        try {\n          await queryFulfilled\n        } catch {\n          patch.undo()\n        }\n      },\n    }),\n  }),\n})\n```\n\nKeep optimistic and pessimistic cache updates inside endpoint lifecycle handlers so they stay coupled to the request.\n\n## Common Mistakes\n\n### CRITICAL Creating multiple API slices for one backend\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst postsApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getUsers: build.query<User[], void>({ query: () => 'users' }),\n  }),\n})\n```\n\nOne API slice per base URL preserves invalidation behavior and avoids duplicated middleware work.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx\n\n### HIGH Forgetting `api.reducer` or `api.middleware`\n\nWrong:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n```\n\nRTK Query hooks need both the reducer and middleware to manage cache state and request lifecycles.\n\nSource: reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx\n\n### MEDIUM Persisting browser API cache by default\n\nWrong:\n\n```ts\nconst storage = window.localStorage\n\nconst persistConfig = {\n  key: 'root',\n  storage,\n}\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n```\n\nPersisting RTK Query cache in browsers often keeps stale data around longer than users expect; treat persistence as a special case, not the default.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx\n\n### HIGH Patching cache from components\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst dispatch = useAppDispatch()\n\nuseEffect(() => {\n  dispatch(\n    api.util.updateQueryData('getPosts', undefined, (draft) => {\n      draft.push({ id: 'p3', title: 'Patched from component' })\n    }),\n  )\n}, [dispatch])\n```\n\nCorrect:\n\n```ts\nupdatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n  query: ({ id, title }) => ({\n    url: `posts/${id}`,\n    method: 'PATCH',\n    body: { title },\n  }),\n  async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n    const patch = dispatch(\n      api.util.updateQueryData('getPosts', undefined, (draft) => {\n        const post = draft.find((item) => item.id === id)\n        if (post) {\n          post.title = title\n        }\n      }),\n    )\n\n    try {\n      await queryFulfilled\n    } catch {\n      patch.undo()\n    }\n  },\n})\n```\n\nComponent-level cache patches drift away from the mutation lifecycle that should own them.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx\n\n### HIGH Expecting invalidation to refetch unsubscribed queries\n\nWrong:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nconst subscription = store.dispatch(api.endpoints.getPosts.initiate())\nsubscription.unsubscribe()\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nCorrect:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nstore.dispatch(api.endpoints.getPosts.initiate())\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nInvalidation only refetches actively subscribed queries; if no component is using that cache entry, RTK Query drops it and fetches again next time it is needed.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx\n\n## References\n\n- [Endpoint lifecycle details and cache tradeoffs](references/endpoint-lifecycle.md)\n","packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md":"# Endpoint Lifecycle\n\n## Invalidation rule\n\nWhen a mutation invalidates tags:\n\n- active subscribers refetch\n- inactive cache entries are removed\n- removed entries fetch again only when something subscribes later\n\nThat behavior is deliberate; invalidation is not a background \"refresh everything\" switch.\n\n## Document cache tradeoff\n\nRTK Query is a document cache, not a normalized entity graph cache.\n\nUse RTK Query by default when:\n\n- the data comes from request/response APIs\n- document caching is acceptable\n- tag invalidation and endpoint lifecycles solve the problem\n\nReach for a different tool when:\n\n- the real requirement is a normalized graph cache\n- the stack already has a domain-specific normalized client that fits better\n\nIf normalized caching is mandatory and no better library is already in the stack, a slice plus thunk flow may be the fallback.\n\n## Useful endpoint options\n\n- `providesTags`: tell RTK Query what cache entries this query represents\n- `invalidatesTags`: tell RTK Query what a mutation dirties\n- `onQueryStarted`: optimistic and pessimistic updates tied to a request\n- `onCacheEntryAdded`: long-lived subscriptions such as streaming data\n- `keepUnusedDataFor`: how long inactive cache entries stay around\n","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md":"---\nname: model-redux-state/build-slices-and-selectors\ndescription: >\n  Use this when authoring or refactoring slices with createSlice, selectors,\n  create.asyncThunk, entity adapters, or lazy reducer injection. Covers\n  Immer-backed mutation syntax, slice selectors, getSelectors, injectInto,\n  withLazyLoadedSlices, and current RTK 2 slice patterns.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - model-redux-state/design-state-ownership\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createSlice.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/combineSlices.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/immer-reducers.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Build Slices And Selectors\n\n## Setup\n\n```ts\n// file: src/app/createAppSlice.ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nexport const createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\n// file: src/features/posts/postsSlice.ts\nimport { createSelector } from '@reduxjs/toolkit'\nimport { createAppSlice } from '../../app/createAppSlice'\n\ntype PostsState = {\n  items: { id: string; title: string; published: boolean }[]\n  status: 'idle' | 'pending' | 'succeeded' | 'failed'\n}\n\nconst initialState: PostsState = {\n  items: [],\n  status: 'idle',\n}\n\nexport const postsSlice = createAppSlice({\n  name: 'posts',\n  initialState,\n  reducers: (create) => ({\n    postAdded: create.reducer<{ id: string; title: string }>(\n      (state, action) => {\n        state.items.push({ ...action.payload, published: false })\n      },\n    ),\n    fetchPosts: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/posts')\n        return (await response.json()) as {\n          id: string\n          title: string\n          published: boolean\n        }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'succeeded'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n  selectors: {\n    selectPosts: (state) => state.items,\n    selectPublishedPosts: createSelector(\n      [(state: PostsState) => state.items],\n      (items) => items.filter((post) => post.published),\n    ),\n  },\n})\n\nexport const { postAdded, fetchPosts } = postsSlice.actions\nexport const { selectPosts, selectPublishedPosts } = postsSlice.selectors\n```\n\n## Core Patterns\n\n### Use mutating logic inside slice reducers\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string; text: string; done: boolean }[],\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.push({ ...action.payload, done: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.done = !todo.done\n      }\n    },\n  },\n})\n```\n\nImmer is the default inside `createSlice`; write the reducer logic directly instead of copying arrays and objects by hand.\n\n### Define selectors in the slice when they belong to the slice\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n  selectors: {\n    selectValue: (state) => state.value,\n    selectIsPositive: (state) => state.value > 0,\n  },\n})\n\nconst { selectValue, selectIsPositive } = counterSlice.selectors\n```\n\nSlice selectors keep state-location knowledge next to the slice.\n\n### Use `create.asyncThunk` when the async lifecycle belongs to the slice\n\n```ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nconst createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\nconst usersSlice = createAppSlice({\n  name: 'users',\n  initialState: {\n    items: [] as { id: string; name: string }[],\n    status: 'idle' as 'idle' | 'pending' | 'failed',\n  },\n  reducers: (create) => ({\n    fetchUsers: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/users')\n        return (await response.json()) as { id: string; name: string }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'idle'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n})\n```\n\nUse this when the async lifecycle handlers naturally live with the slice; otherwise regular `createAsyncThunk` is still fine.\n\n### Use entity adapters and lazy injection for scalable slices\n\n```ts\nimport {\n  combineSlices,\n  createEntityAdapter,\n  createSlice,\n} from '@reduxjs/toolkit'\n\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n\nconst booksSlice = createSlice({\n  name: 'books',\n  initialState: booksAdapter.getInitialState(),\n  reducers: {\n    booksReceived: booksAdapter.setAll,\n  },\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices().withLazyLoadedSlices<LazyLoadedSlices>()\n\ndeclare module './rootReducer' {\n  export interface LazyLoadedSlices {}\n}\n\nconst injectedBooksSlice = booksSlice.injectInto(rootReducer)\n\nconst selectors = booksAdapter.getSelectors(\n  (state: ReturnType<typeof rootReducer.selector.original>) =>\n    injectedBooksSlice.selectSlice(state),\n)\n```\n\nEntity adapters standardize normalized collections, and `injectInto` lets a slice stay aware of its injected location.\n\n## Common Mistakes\n\n### CRITICAL Using mutating logic outside slice reducers\n\nWrong:\n\n```ts\ntype Todo = { id: string; text: string }\n\nexport function addTodo(todos: Todo[], todo: Todo) {\n  todos.push(todo)\n  return todos\n}\n```\n\nCorrect:\n\n```ts\ntype Todo = { id: string; text: string }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as Todo[],\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nMutation syntax is only safe inside Immer-backed reducer contexts such as `createSlice` and `createReducer`.\n\nSource: reduxjs/redux-toolkit:docs/usage/immer-reducers.md\n\n### HIGH Writing hand-written switch reducers as the default\n\nWrong:\n\n```ts\nexport default function todosReducer(\n  state = initialState,\n  action: { type: string; payload?: Todo },\n) {\n  switch (action.type) {\n    case 'todos/todoAdded':\n      return state.concat(action.payload as Todo)\n    default:\n      return state\n  }\n}\n```\n\nCorrect:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nHand-written reducers are an escape hatch for proven bottlenecks, not the normal thing an agent should generate in RTK code.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Writing RTK 1.x object syntax for `extraReducers`\n\nWrong:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: {\n    [fetchPosts.fulfilled.type]: (state, action) => {\n      state.items = action.payload\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: (builder) => {\n    builder.addCase(fetchPosts.fulfilled, (state, action) => {\n      state.items = action.payload\n    })\n  },\n})\n```\n\nRTK 2 removed the object form; agents trained on RTK 1.x still generate it.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Assuming `entity.id` exists for every collection\n\nWrong:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>()\n```\n\nCorrect:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n```\n\nAdapters default to `entity.id`; collections keyed by another field must provide `selectId`.\n\nSource: reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx\n\n## References\n\n- [Slice selectors, async creators, and lazy injection details](references/slice-patterns.md)\n","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md":"# Slice Patterns\n\n## `getSelectors` for alternate mounting points\n\n```ts\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {},\n  selectors: {\n    selectValue: (state) => state.value,\n  },\n})\n\nexport const { selectValue } = counterSlice.getSelectors(\n  (state: { customCounter: { value: number } }) => state.customCounter,\n)\n```\n\nUse `getSelectors` when the slice is not mounted at its default `reducerPath`.\n\n## `withLazyLoadedSlices` and `injectInto`\n\n```ts\nimport { combineSlices, createSlice, type WithSlice } from '@reduxjs/toolkit'\n\nconst staticSlice = createSlice({\n  name: 'static',\n  initialState: { ready: true },\n  reducers: {},\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices(staticSlice).withLazyLoadedSlices<LazyLoadedSlices>()\n\nconst lazySlice = createSlice({\n  name: 'lazy',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\ndeclare module '../rootReducer' {\n  export interface LazyLoadedSlices extends WithSlice<typeof lazySlice> {}\n}\n\nexport const injectedLazySlice = lazySlice.injectInto(rootReducer)\n```\n\nThis keeps RootState types aware of reducers that will be injected later.\n\n## Selector caveat\n\nIf a selector depends on caller-specific arguments and must be memoized per caller, prefer a selector factory outside `createSlice.selectors`. `createSlice.selectors` gives you one selector instance, not a selector factory.\n","packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md":"---\nname: model-redux-state/design-state-ownership\ndescription: >\n  Use this when deciding whether data belongs in Redux, component state, router\n  state, or another external source. Covers state ownership, authority\n  boundaries, slice sizing, and when to move or split data as the app evolves.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md'\n---\n\n# Design State Ownership\n\n## Setup\n\n```tsx\nimport { useState } from 'react'\nimport { createSlice } from '@reduxjs/toolkit'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string; content: string }[],\n  reducers: {\n    postAdded(\n      state,\n      action: { payload: { id: string; title: string; content: string } },\n    ) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst { postAdded } = postsSlice.actions\n\nexport function AddPostForm() {\n  const [title, setTitle] = useState('')\n  const [content, setContent] = useState('')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(postAdded({ id: 'p1', title, content }))\n      }}\n    >\n      <input value={title} onChange={(event) => setTitle(event.target.value)} />\n      <textarea\n        value={content}\n        onChange={(event) => setContent(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep editable form state local until the user commits it\n\n```tsx\nimport { useState } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\nimport { profileSaved } from './profileSlice'\n\nexport function ProfileForm() {\n  const [displayName, setDisplayName] = useState('Lenz')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(profileSaved({ displayName }))\n      }}\n    >\n      <input\n        value={displayName}\n        onChange={(event) => setDisplayName(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\nPrefer Redux for shared, durable app state, not every keystroke.\n\n### Keep URL state with the router and combine it at the edge\n\n```tsx\nimport { createSelector } from '@reduxjs/toolkit'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppSelector } from '../../app/hooks'\n\ntype RootState = {\n  posts: {\n    items: { id: string; title: string; published: boolean }[]\n  }\n}\n\nconst selectPosts = (state: RootState) => state.posts.items\n\nconst selectVisiblePosts = createSelector(\n  [selectPosts, (_state: RootState, filter: string) => filter],\n  (posts, filter) =>\n    filter === 'published' ? posts.filter((post) => post.published) : posts,\n)\n\nexport function PostsList() {\n  const [searchParams] = useSearchParams()\n  const filter = searchParams.get('filter') ?? 'all'\n  const posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n\n  return <div>{posts.length}</div>\n}\n```\n\nIf the router already owns a piece of state, pass it into selectors or combine it in the component instead of syncing it into Redux.\n\n### Re-size slices when access patterns change\n\n```ts\nimport { combineReducers, createSlice } from '@reduxjs/toolkit'\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: { items: [] as { id: string; title: string }[] },\n  reducers: {},\n})\n\nexport const rootReducer = combineReducers({\n  auth: authSlice.reducer,\n  posts: postsSlice.reducer,\n})\n```\n\nRevisit slice size over time; unrelated data should split apart, and data constantly stitched together in every component may belong closer together.\n\n## Common Mistakes\n\n### MEDIUM Putting form editing state in Redux\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nconst selectDraftTitle = (state: { draft: { title: string } }) =>\n  state.draft.title\n\nconst title = useAppSelector(selectDraftTitle)\n```\n\nCorrect:\n\n```tsx\nconst [title, setTitle] = useState('')\n\n<input value={title} onChange={(event) => setTitle(event.target.value)} />\n```\n\nPer-keystroke dispatching adds global complexity for data that usually lives in one component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Synchronizing router or URL state into Redux\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppDispatch } from '../../app/hooks'\n\nfunction PostsPage() {\n  const [searchParams] = useSearchParams()\n  const dispatch = useAppDispatch()\n\n  useEffect(() => {\n    dispatch(filterChanged(searchParams.get('filter') ?? 'all'))\n  }, [dispatch, searchParams])\n\n  return null\n}\n```\n\nCorrect:\n\n```tsx\nconst filter = searchParams.get('filter') ?? 'all'\nconst posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n```\n\nURL state already has an authoritative owner; duplicating it into Redux creates two sources of truth.\n\nSource: maintainer interview\n\n### HIGH Naming state after components\n\nWrong:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst loginReducer = (state = { open: false }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  loginScreen: loginReducer,\n  postsList: postsReducer,\n})\n```\n\nCorrect:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst authReducer = (state = { userId: null as string | null }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  auth: authReducer,\n  posts: postsReducer,\n})\n```\n\nStore keys should describe data or domain concepts, not the current component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Letting slice boundaries fossilize\n\nWrong:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\ntype AppNotification = { id: string; message: string }\n\nconst appSlice = createSlice({\n  name: 'app',\n  initialState: {\n    auth: { userId: null as string | null },\n    posts: [] as Post[],\n    notifications: [] as AppNotification[],\n  },\n  reducers: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as Post[],\n  reducers: {},\n})\n```\n\nWhen unrelated data is welded together, every change point gets noisier; split or merge slices as actual access patterns demand.\n\nSource: maintainer interview\n\n### HIGH Blindly spreading payloads into state\n\nWrong:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  return { ...state, ...action.payload }\n}\n```\n\nCorrect:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  state.id = action.payload.id\n  state.name = action.payload.name\n}\n```\n\nReducers should own the slice shape instead of treating payloads as trusted state patches.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n## References\n\n- [State ownership heuristics](references/state-ownership.md)\n","packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md":"# State Ownership Heuristics\n\n## Choose the owner, then the tool\n\n| Kind of state                              | Default owner   | Typical tool                             |\n| ------------------------------------------ | --------------- | ---------------------------------------- |\n| Editable form fields                       | Component       | `useState`                               |\n| Shared mutable app data                    | Redux           | Slice state                              |\n| Server cache                               | RTK Query       | `createApi`                              |\n| URL, pathname, search params               | Router          | Router APIs plus selector inputs         |\n| Browser-only authority like `localStorage` | External source | Read at boundaries, then dispatch events |\n\n## Good reasons to move data into Redux\n\n- Multiple distant parts of the UI need the same mutable data.\n- You need time-travel debugging or a stable action history.\n- The reducer should own transitions because they mix old store state with new inputs.\n\n## Reasons to keep data out of Redux\n\n- Another system already owns it, such as the router.\n- It only matters during editing inside one component tree.\n- It is server cache and RTK Query fits the use case better.\n\n## Re-evaluate slice size\n\n- If data is constantly stitched together outside reducers, it may belong closer together.\n- If unrelated updates keep touching the same slice, split the slice by domain ownership.\n","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md":"---\nname: orchestrate-side-effects/handle-side-effects\ndescription: >\n  Use this when choosing between RTK Query, createAsyncThunk, handwritten\n  thunks, and createListenerMiddleware. Covers imperative versus reactive\n  workflows, listener middleware setup, and keeping side effects out of\n  reducers and UI components.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n---\n\n# Handle Side Effects\n\n## Setup\n\n```ts\nimport {\n  configureStore,\n  createListenerMiddleware,\n  createSlice,\n} from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveStarted(state) {\n      state.status = 'idle'\n    },\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nexport const store = configureStore({\n  reducer: {\n    docs: docsSlice.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\nexport const startAppListening = listenerMiddleware.startListening.withTypes<\n  RootState,\n  AppDispatch\n>()\n```\n\n## Core Patterns\n\n### Use RTK Query for server cache by default\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf the problem is server data that should be cached and re-used, start with RTK Query instead of a thunk.\n\n### Use `createAsyncThunk` for imperative workflows\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Draft = { title: string }\n\nexport const draftSaved = createAsyncThunk(\n  'drafts/save',\n  async (draft: Draft) => {\n    const response = await fetch('/api/drafts', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(draft),\n    })\n\n    return (await response.json()) as { id: string; title: string }\n  },\n)\n\nconst draftsSlice = createSlice({\n  name: 'drafts',\n  initialState: { status: 'idle' as 'idle' | 'pending' | 'failed' },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(draftSaved.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(draftSaved.fulfilled, (state) => {\n        state.status = 'idle'\n      })\n      .addCase(draftSaved.rejected, (state) => {\n        state.status = 'failed'\n      })\n  },\n})\n```\n\nUse a thunk when you need one imperative async workflow with `dispatch` and `getState`.\n\n### Use listener middleware for reactive workflows\n\n```ts\nimport { createListenerMiddleware, createSlice } from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst notificationsSlice = createSlice({\n  name: 'notifications',\n  initialState: [] as string[],\n  reducers: {\n    notificationQueued(state, action: { payload: string }) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nlistenerMiddleware.startListening({\n  actionCreator: docsSlice.actions.saveFinished,\n  effect: async (_action, listenerApi) => {\n    listenerApi.dispatch(\n      notificationsSlice.actions.notificationQueued('Document saved'),\n    )\n  },\n})\n```\n\nListeners fit workflows that react to future actions or state changes over time instead of driving one imperative request from a single callsite.\n\n## Common Mistakes\n\n### CRITICAL Running side effects inside reducers\n\nWrong:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string }[],\n  reducers: {\n    todoSaved(state, action: { payload: { id: string } }) {\n      fetch('/api/todos', { method: 'POST' })\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\nconst todoSaved = createAsyncThunk(\n  'todos/save',\n  async (todo: { id: string }) => {\n    await fetch('/api/todos', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(todo),\n    })\n    return todo\n  },\n)\n```\n\nReducers must stay pure even when Immer is available.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using thunks to watch future state changes\n\nWrong:\n\n```ts\nexport const waitForSave =\n  () =>\n  async (_dispatch: unknown, getState: () => { docs: { status: string } }) => {\n    while (getState().docs.status !== 'saved') {\n      await new Promise((resolve) => setTimeout(resolve, 100))\n    }\n  }\n```\n\nCorrect:\n\n```ts\nstartAppListening({\n  predicate: (_action, currentState) => currentState.docs.status === 'saved',\n  effect: async () => {\n    console.log('Document saved')\n  },\n})\n```\n\nPolling inside thunks fights the architecture; listener middleware is the reactive tool.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n### HIGH Appending listener middleware after the default checks\n\nWrong:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(listenerMiddleware.middleware),\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n```\n\nListener add and remove actions may carry functions, so the listener middleware needs to run before serializability checks.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n## References\n\n- [Listener helpers and decision heuristics](references/listener-workflows.md)\n","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md":"# Listener Workflows\n\n## Decision table\n\n| Need                                                       | Reach for                     |\n| ---------------------------------------------------------- | ----------------------------- |\n| Cached server data                                         | RTK Query                     |\n| One imperative async workflow with `dispatch` / `getState` | `createAsyncThunk` or a thunk |\n| React to later actions or state transitions                | `createListenerMiddleware`    |\n\nA good app often mixes imperative and reactive workflows. The split is by job, not by ideology.\n\n## Useful listener helpers\n\n- `predicate`: react to any action when a state condition becomes true\n- `condition`: wait until a condition becomes true before continuing\n- `take`: wait for the next matching action\n- `cancelActiveListeners`: cancel older instances of the same workflow\n- `fork`: start a child task\n\n## Example: cancel stale work\n\n```ts\nstartAppListening({\n  actionCreator: searchRequested,\n  effect: async (action, listenerApi) => {\n    listenerApi.cancelActiveListeners()\n    await listenerApi.delay(250)\n    listenerApi.dispatch(searchStarted(action.payload))\n  },\n})\n```\n\nThis is the kind of long-lived reactive behavior that does not fit a thunk well.\n"},"files":{"packages/rtk-codemods/README.md":"# RTK Codemods\n\nA collection of codemods for updating legacy Redux Toolkit API usage patterns to modern patterns.\n\n## Usage\n\nTo run a specific codemod from this project, you would run the following:\n\n```bash\nnpx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Transforms\n\n<!--TRANSFORMS_START-->\n\n- [createReducerBuilder](transforms/createReducerBuilder/README.md)\n- [createSliceBuilder](transforms/createSliceBuilder/README.md)\n- [createSliceReducerBuilder](transforms/createSliceReducerBuilder/README.md)\n\n<!--TRANSFORMS_END-->\n\n## Contributing\n\n### Installation\n\n- clone the repo\n- change into the repo directory\n- `pnpm install`\n\n### Running tests\n\n- `pnpm --filter @reduxjs/rtk-codemods test`\n","packages/rtk-codemods/transforms/createReducerBuilder/README.md":"# createReducerBuilder\n\nRewrites uses of the \"object\" syntax for Redux Toolkit's `createReducer` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\ncreateReducer(initialState, {\n  [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n    // stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded](state: SliceState, action: PayloadAction<string>) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createReducerBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\ncreateReducer(initialState, {\n  [todoAdded1a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded1b]: (state, action) => action.payload,\n  [todoAdded1c + 'test']: (state, action) => {\n    // stuff\n  },\n  [todoAdded1d](state, action) {\n    // stuff\n  },\n  [todoAdded1e]: function (state, action) {\n    // stuff\n  },\n  todoAdded1f: (state, action) => {\n    //stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded2a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded2b](state, action) {\n    // stuff\n  },\n  [todoAdded2c]: function (state, action) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createReducerBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded1a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n  builder.addCase(todoAdded1c + 'test', (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1d, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1e, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1f, (state, action) => {\n    //stuff\n  })\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded2a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2b, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2c, (state, action) => {\n    // stuff\n  })\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-codemods/transforms/createSliceBuilder/README.md":"# createSliceBuilder\n\nRewrites uses of the `extraReducers` \"object\" syntax for Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n  extraReducers: {\n    [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n  extraReducers: {\n    [todoAdded](state: SliceState, action: PayloadAction<string>) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createSliceBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n  extraReducers: {\n    [todoAdded1a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded1b]: (state, action) => action.payload,\n    [todoAdded1c + 'test']: (state, action) => {\n      // stuff\n    },\n    [todoAdded1d](state, action) {\n      // stuff\n    },\n    [todoAdded1e]: function (state, action) {\n      // stuff\n    },\n    todoAdded1f: (state, action) => {\n      //stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n  extraReducers: {\n    [todoAdded2a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded2b](state, action) {\n      // stuff\n    },\n    [todoAdded2c]: function (state, action) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createSliceBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded1a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n    builder.addCase(todoAdded1c + 'test', (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1d, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1e, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1f, (state, action) => {\n      //stuff\n    })\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded2a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2b, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2c, (state, action) => {\n      // stuff\n    })\n  }\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md":"# createSliceReducerBuilder\n\nRewrites uses of Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax for the `reducers` field, to make it easier to add prepared reducers and thunks inside of `createSlice`.\n\nNote that unlike the `createReducerBuilder` and `createSliceBuilder` transforms (which both were fixes for deprecated/removed overloads), this is entirely optional. You do not _need_ to apply this to an entire codebase unless you specifically want to. Otherwise, feel free to apply to specific slice files as needed.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```\nnpx @reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\n@reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n<!--FIXTURES_TOC_END-->\n\n<!--FIXTURES_CONTENT_START-->\n<!--FIXTURES_CONTENT_END-->\n","packages/rtk-query-codegen-openapi/README.md":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/rtk-incubator/rtk-query/main/logo.png\" width=\"400\" />\n</p>\n<h2 align=\"center\">\nCode Generator\n</h2>\n\n<p align=\"center\">\n   <a href=\"https://discord.gg/0ZcbPKXt5bZ6au5t\" target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/chat-online-green\" alt=\"Discord server\" />\n  </a>\n</p>\n\n### Introduction\n\nThis is a utility library meant to be used with [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) that will generate a typed API client from an OpenAPI schema.\n\n### Documentation\n\n[View the RTK Query Code Generation docs](https://redux-toolkit.js.org/rtk-query/usage/code-generation)\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md":"---\nname: manage-server-data/generate-rtk-query-from-openapi\ndescription: >\n  Use this when generating RTK Query endpoints from OpenAPI schemas with\n  @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints,\n  endpointOverrides, generated tags, and reviewing generated output before it\n  becomes part of the app.\ntype: composition\nlibrary: '@rtk-query/codegen-openapi'\nlibrary_version: '2.2.0'\nrequires:\n  - manage-server-data/adopt-rtk-query\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/types.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/generate.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/README.md'\n---\n\n# Generate RTK Query From OpenAPI\n\n## Setup\n\n```ts\n// file: src/store/emptyApi.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n\n// file: openapi-config.ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n};\n\nexport default config;\n```\n\nRun:\n\n```bash\nnpx @rtk-query/codegen-openapi openapi-config.ts\n```\n\n## Core Patterns\n\n### Generate into an empty shared API\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nCodegen works best when it extends a single RTK Query architecture instead of creating standalone API roots.\n\n### Filter endpoints when the schema is too broad\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/userApi.ts',\n  exportName: 'userApi',\n  hooks: true,\n  filterEndpoints: ['loginUser', /User/],\n};\n\nexport default config;\n```\n\nStart with a narrow slice of the schema if the full surface area is too noisy or the package boundaries differ from the OpenAPI file.\n\n### Use `endpointOverrides` to fix generation results\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n    {\n      pattern: /.*/,\n      parameterFilter: (_name, parameter) => parameter.in !== 'header',\n    },\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n\nexport default config;\n```\n\nReview generated endpoints and override type, parameter, or tag behavior instead of hand-editing the emitted file.\n\n## Common Mistakes\n\n### HIGH Assuming generated tags are already specific enough\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n```\n\nGenerated tags are string-only by default, so they can invalidate more cache than intended.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### HIGH Generating a brand-new API root instead of extending an empty one\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\ntype Pet = { id: string; name: string };\n\nexport const petApi = createApi({\n  reducerPath: 'petApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getPetById: build.query<Pet, string>({\n      query: (id) => `pets/${id}`,\n    }),\n  }),\n});\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nGenerated code should plug into one RTK Query architecture so invalidation and store wiring stay coherent.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### MEDIUM Trusting generated shapes without overrides or review\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n  ],\n};\n```\n\nReal schemas often need type, parameter, or tag correction; treat generated output as reviewed source, not gospel.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n## References\n\n- [Codegen override patterns and review checklist](references/codegen-overrides.md)\n","packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md":"# Codegen Overrides\n\n## Common override targets\n\n| Need                                            | Override                           |\n| ----------------------------------------------- | ---------------------------------- |\n| Query should really be a mutation               | `type: 'mutation'`                 |\n| Header or optional parameters should be omitted | `parameterFilter`                  |\n| Generated tags are too coarse                   | `providesTags` / `invalidatesTags` |\n\n## Example: split one schema into multiple outputs\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFiles: {\n    './src/store/userApi.ts': {\n      exportName: 'userApi',\n      filterEndpoints: [/user/i],\n      hooks: true,\n    },\n    './src/store/orderApi.ts': {\n      exportName: 'orderApi',\n      filterEndpoints: [/order/i],\n      hooks: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Review checklist\n\n- Does every generated endpoint belong in the shared API root?\n- Did any query or mutation type need correction?\n- Are generated tags specific enough for real invalidation needs?\n- Did the schema force parameters into the signature that the app should hide?\n","packages/toolkit/README.md":"# Redux Toolkit\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-toolkit/tests.yml?style=flat-square)\n[![npm version](https://img.shields.io/npm/v/@reduxjs/toolkit.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/toolkit)\n[![npm downloads](https://img.shields.io/npm/dm/@reduxjs/toolkit.svg?style=flat-square&label=RTK+downloads)](https://www.npmjs.com/package/@reduxjs/toolkit)\n\n**The official, opinionated, batteries-included toolset for efficient Redux development**\n\n## Installation\n\n### Create a React Redux App\n\nThe recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).\n\nBoth of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.\n\n```bash\n# Vite with our Redux+TS template\n# (using the `tiged` tool to clone and extract the template)\nnpx tiged reduxjs/redux-templates/packages/vite-template-redux my-app\n\n# Next.js using the `with-redux` template\nnpx create-next-app --example with-redux my-app\n```\n\nFor React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):\n\n```bash\nnpx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app\n```\n\n### An Existing App\n\nRedux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:\n\n```bash\n# NPM\nnpm install @reduxjs/toolkit\n\n# Yarn\nyarn add @reduxjs/toolkit\n```\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n\nThe package includes a precompiled browser ESM build that can be loaded from a `<script type=\"module\">` tag. No-bundler browser usage also needs an import map, because the build imports Redux Toolkit's dependencies by package name. See the [Getting Started docs](https://redux-toolkit.js.org/introduction/getting-started#an-existing-app) for an example.\n\n## Documentation\n\nThe Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit.\n\nThe Redux core docs at https://redux.js.org includes the full Redux tutorials, as well usage guides on general Redux patterns.\n\n## Purpose\n\nThe **Redux Toolkit** package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:\n\n- \"Configuring a Redux store is too complicated\"\n- \"I have to add a lot of packages to get Redux to do anything useful\"\n- \"Redux requires too much boilerplate code\"\n\nWe can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.\n\nBecause of that, this package is deliberately limited in scope. It does _not_ address concepts like \"reusable encapsulated Redux modules\", folder or file structures, managing entity relationships in the store, and so on.\n\nRedux Toolkit also includes a powerful data fetching and caching capability that we've dubbed \"RTK Query\". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.\n\n## What's Included\n\nRedux Toolkit includes these APIs:\n\n- `configureStore()`: wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, add whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.\n- `createReducer()`: lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/mweststrate/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.\n- `createAction()`: generates an action creator function for the given action type string. The function itself has `toString()` defined, so that it can be used in place of the type constant.\n- `createSlice()`: combines `createReducer()` + `createAction()`. Accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.\n- `combineSlices()`: combines multiple slices into a single reducer, and allows \"lazy loading\" of slices after initialisation.\n- `createListenerMiddleware()`: lets you define \"listener\" entries that contain an \"effect\" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes. A lightweight alternative to Redux async middleware like sagas and observables.\n- `createAsyncThunk()`: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/resolved/rejected` action types based on that promise\n- `createEntityAdapter()`: generates a set of reusable reducers and selectors to manage normalized data in the store\n- The `createSelector()` utility from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.\n\nFor details, see [the Redux Toolkit API Reference section in the docs](https://redux-toolkit.js.org/api/configureStore).\n\n## RTK Query\n\n**RTK Query** is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.\n\nRTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](https://redux.js.org/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.\n\nRTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:\n\n```ts no-transpile\nimport { createApi } from '@reduxjs/toolkit/query'\n\n/* React-specific entry point that automatically generates\n   hooks corresponding to the defined endpoints */\nimport { createApi } from '@reduxjs/toolkit/query/react'\n```\n\n### What's included\n\nRTK Query includes these APIs:\n\n- `createApi()`: The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with \"one API slice per base URL\" as a rule of thumb.\n- `fetchBaseQuery()`: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.\n- `<ApiProvider />`: Can be used as a Provider if you do not already have a Redux store.\n- `setupListeners()`: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.\n\nSee the [**RTK Query Overview**](https://redux-toolkit.js.org/rtk-query/overview) page for more details on what RTK Query is, what problems it solves, and how to use it.\n\n## Contributing\n\nPlease refer to our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Redux Toolkit.\n","packages/toolkit/scripts/issue-triage/README.md":"# GitHub Issues Triage Tool\n\nA TypeScript-based tool for automatically triaging and categorizing GitHub issues in the Redux Toolkit repository.\n\n## Features\n\n- Fetches open issues from GitHub API\n- Categorizes issues based on labels, titles, and content\n- Generates markdown reports with statistics\n- Caches data to avoid API rate limiting\n- Supports both Node.js and Bun runtimes\n\n## Setup\n\n1. Install dependencies:\n\n```bash\nnpm install\n```\n\n2. Build the TypeScript code:\n\n```bash\nnpm run build\n```\n\n## Usage\n\nRun the triage tool:\n\n```bash\nnpm start\n```\n\nOr for development (builds and runs):\n\n```bash\nnpm run dev\n```\n\n### Using Cached Data\n\nThe tool automatically saves fetched data to `cache/issues-data.json` after each run. To use cached data instead of fetching fresh data (useful for development and testing):\n\n```bash\nnpm start -- --use-cache\n```\n\nOr with the dev command:\n\n```bash\nnpm run dev -- --use-cache\n```\n\n**Note:** The `--use-cache` flag is primarily for development/testing purposes. It loads data from the cache file instantly without making API calls. If the cache file doesn't exist or is corrupted, the tool will automatically fetch fresh data.\n\n## Project Structure\n\n```\nscripts/issue-triage/\n├── src/           # TypeScript source files\n├── dist/          # Compiled JavaScript output\n├── cache/         # Cached JSON data from GitHub\n├── reports/       # Generated triage reports\n├── package.json   # Project dependencies\n└── tsconfig.json  # TypeScript configuration\n```\n\n## Requirements\n\n- Node.js 18+ or Bun 1.0+\n- TypeScript 5.0+\n\n## Development\n\nThis tool is built with TypeScript using ES modules (ESM). The configuration supports both Node.js and Bun runtimes for maximum flexibility.\n","packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md":"---\nname: build-modern-redux-apps/modern-redux\ndescription: >\n  Use this when setting up a new Redux Toolkit app or modernizing an existing\n  React + Redux codebase. Covers configureStore, Provider wiring, typed hooks,\n  hooks-first React-Redux usage, feature folders, and the correct store lifetime\n  for SPA and SSR-heavy React environments.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/typescript.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/nextjs.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Modern Redux\n\n## Setup\n\n```tsx\n// file: src/features/counter/counterSlice.ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nexport const counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\nexport const { increment } = counterSlice.actions\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterSlice.reducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\n// file: src/app/hooks.ts\nimport { useDispatch, useSelector } from 'react-redux'\nimport type { AppDispatch, RootState } from './store'\n\nexport const useAppDispatch = useDispatch.withTypes<AppDispatch>()\nexport const useAppSelector = useSelector.withTypes<RootState>()\n\n// file: src/features/counter/Counter.tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n\n// file: src/main.tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { Counter } from './features/counter/Counter'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <Provider store={store}>\n    <Counter />\n  </Provider>,\n)\n```\n\n## Core Patterns\n\n### Keep React components on hooks, not wrappers\n\n```tsx\nimport { postAdded, selectPosts } from './postsSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector(selectPosts)\n  const dispatch = useAppDispatch()\n\n  return (\n    <button\n      onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}\n    >\n      {posts.length}\n    </button>\n  )\n}\n```\n\nHooks are the default React-Redux integration for new code.\n\n### Create the store inside the provider for SSR-heavy React apps\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      counter: counterSlice.reducer,\n    },\n  })\n\nexport type AppStore = ReturnType<typeof makeStore>\nexport type RootState = ReturnType<AppStore['getState']>\nexport type AppDispatch = AppStore['dispatch']\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nIn SSR-heavy React frameworks, create a store per request and keep that instance stable across renders.\n\n### Keep app wiring in `app/` and feature logic in feature folders\n\n```text\nsrc/\n  app/\n    store.ts\n    hooks.ts\n  features/\n    posts/\n      postsSlice.ts\n      PostsList.tsx\n    users/\n      usersSlice.ts\n      UsersList.tsx\n```\n\nThis keeps store wiring centralized and feature logic colocated.\n\n## Common Mistakes\n\n### HIGH Importing the store in React components\n\nWrong:\n\n```tsx\nimport { store } from '../../app/store'\n\nexport function PostsList() {\n  const posts = store.getState().posts\n  return <div>{posts.length}</div>\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts)\n  return <div>{posts.length}</div>\n}\n```\n\nReact components should read through context and hooks; direct store imports are a separate escape hatch for non-React integrations, not the default UI pattern.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Defaulting to `connect()` in new React code\n\nWrong:\n\n```tsx\nimport { connect } from 'react-redux'\n\nconst increment = () => ({ type: 'counter/increment' as const })\nconst mapState = (state: { counter: { value: number } }) => ({\n  value: state.counter.value,\n})\nconst mapDispatch = { increment }\n\nfunction Counter({\n  value,\n  increment,\n}: ReturnType<typeof mapState> & typeof mapDispatch) {\n  return <button onClick={() => increment()}>{value}</button>\n}\n\nexport default connect(mapState, mapDispatch)(Counter)\n```\n\nCorrect:\n\n```tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n```\n\nHooks are the modern default, simpler to type, and the maintainers explicitly want agents to steer new code away from `connect`.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Recreating the store during render in SSR-heavy apps\n\nWrong:\n\n```tsx\n'use client'\n\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const store = makeStore()\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCorrect:\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nA new store on every render loses client state, while a module singleton can leak across requests on the server.\n\nSource: reduxjs/redux-toolkit:docs/usage/nextjs.mdx\n\n### HIGH Keeping `createStore` boilerplate as the default\n\nWrong:\n\n```ts\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nconst rootReducer = combineReducers({\n  counter: counterReducer,\n})\n\nexport const store = createStore(rootReducer, applyMiddleware(thunk))\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer,\n  },\n})\n```\n\nManual store setup works, but it throws away RTK's default middleware, dev checks, and the current recommended baseline.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n## References\n\n- [Store lifetime and framework boundaries](references/store-lifetime.md)\n","packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md":"# Store Lifetime\n\n## Decision table\n\n| Environment                | Default store shape                     | Why                                                                                                  |\n| -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |\n| Client-only SPA            | One module-level singleton store        | There is one browser session and no cross-request leakage risk.                                      |\n| SSR-heavy React app        | `makeStore()` plus provider-local state | Each request needs its own store instance, but that instance must stay stable across client renders. |\n| Non-React integration code | Direct store access can be acceptable   | This is outside the React context boundary and should stay out of UI components.                     |\n\n## SPA pattern\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n```\n\nUse this for classic browser SPAs.\n\n## SSR-heavy React pattern\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      posts: postsSlice.reducer,\n    },\n  })\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCreate the store per request, then keep it stable inside the provider component.\n","packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md":"---\nname: build-modern-redux-apps/redux-dataflow\ndescription: >\n  Use this when you need the Redux event -> reducer -> selector -> render loop,\n  event-style actions, reducer-owned state transitions, derived data, or a\n  debugging model for Redux Toolkit apps.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Redux Dataflow\n\n## Setup\n\n```ts\nimport { configureStore, createSelector, createSlice } from '@reduxjs/toolkit'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as { id: string; title: string; published: boolean }[],\n    filter: 'all' as 'all' | 'published',\n  },\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.items.push({ ...action.payload, published: false })\n    },\n    postPublished(state, action: { payload: { id: string } }) {\n      const post = state.items.find((item) => item.id === action.payload.id)\n      if (post) {\n        post.published = true\n      }\n    },\n    filterChanged(state, action: { payload: 'all' | 'published' }) {\n      state.filter = action.payload\n    },\n  },\n})\n\nconst store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n\nconst selectPostsState = (state: RootState) => state.posts\nconst selectVisiblePosts = createSelector([selectPostsState], (postsState) =>\n  postsState.filter === 'all'\n    ? postsState.items\n    : postsState.items.filter((post) => post.published),\n)\n\nstore.dispatch(postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' }))\nstore.dispatch(postsSlice.actions.postPublished({ id: 'p1' }))\n\nconst visiblePosts = selectVisiblePosts(store.getState())\nconsole.log(visiblePosts)\n```\n\n## Core Patterns\n\n### Dispatch events, not setters\n\n```ts\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string }[],\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.push(action.payload)\n    },\n    postRemoved(state, action: { payload: { id: string } }) {\n      return state.filter((post) => post.id !== action.payload.id)\n    },\n    postUpdated(\n      state,\n      action: { payload: { id: string; changes: Partial<{ title: string }> } },\n    ) {\n      const post = state.find((item) => item.id === action.payload.id)\n      if (post && action.payload.changes.title) {\n        post.title = action.payload.changes.title\n      }\n    },\n  },\n})\n\npostsSlice.actions.postAdded({ id: 'p1', title: 'Draft' })\n```\n\nEvent-style actions explain what happened in the UI instead of hiding the transition behind a generic setter.\n\n### Let reducers combine old store data with new outside data\n\n```ts\nimport { createEntityAdapter, createSlice } from '@reduxjs/toolkit'\n\nconst postsAdapter = createEntityAdapter<{ id: string; title: string }>()\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: postsAdapter.getInitialState(),\n  reducers: {\n    postsReceived(state, action: { payload: { id: string; title: string }[] }) {\n      postsAdapter.upsertMany(state, action.payload)\n    },\n  },\n})\n\nconst incomingPosts = [\n  { id: 'p1', title: 'Draft' },\n  { id: 'p2', title: 'Published' },\n]\n\npostsSlice.actions.postsReceived(incomingPosts)\n```\n\nIf a transition mixes current store state with new external data, dispatch the new external data and let the reducer own the merge.\n\n### Derive values with selectors instead of storing duplicates\n\n```ts\nimport { createSelector } from '@reduxjs/toolkit'\n\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectFilter = (state: RootState) => state.posts.filter\n\nexport const selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nSelectors keep a single source of truth in state while still exposing the shapes the UI needs.\n\n## Common Mistakes\n\n### CRITICAL Mutating selected state outside reducers\n\nWrong:\n\n```ts\nconst post = selectPostById(store.getState(), 'p1')\n\nif (post) {\n  post.title = 'Changed in place'\n}\n```\n\nCorrect:\n\n```ts\nstore.dispatch(\n  postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),\n)\n```\n\nObjects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using setter-style actions instead of event-style actions\n\nWrong:\n\n```ts\nconst nextPosts = [\n  ...selectPosts(store.getState()),\n  { id: 'p2', title: 'Write docs' },\n]\nstore.dispatch(setPosts(nextPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postAdded({ id: 'p2', title: 'Write docs' }))\n```\n\nActions should describe events, not ask reducers to blindly replace state with a precomputed value.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Combining store state before dispatch\n\nWrong:\n\n```ts\nconst currentPosts = selectPosts(store.getState())\nconst mergedPosts = [\n  ...currentPosts.filter(\n    (currentPost) =>\n      !incomingPosts.some((incomingPost) => incomingPost.id === currentPost.id),\n  ),\n  ...incomingPosts,\n]\n\nstore.dispatch(postsReplaced(mergedPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postsReceived(incomingPosts))\n```\n\nIf the next state depends on current store state, the reducer should own that combination logic; only authoritative external snapshots should replace state wholesale.\n\nSource: maintainer interview\n\n### HIGH Ignoring current state in async reducers\n\nWrong:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  state.status = 'succeeded'\n  state.items = action.payload\n})\n```\n\nCorrect:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  if (state.status === 'pending') {\n    state.status = 'succeeded'\n    state.items = action.payload\n  }\n})\n```\n\nReducers that treat every lifecycle action as valid can move the slice into impossible states or let stale requests win.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### MEDIUM Storing derived values in state\n\nWrong:\n\n```ts\nconst initialState = {\n  items: [] as Post[],\n  visiblePosts: [] as Post[],\n}\n```\n\nCorrect:\n\n```ts\nconst selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nDerived values drift out of sync quickly; keep the raw state and derive the view shape.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n","packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md":"---\nname: evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps\ndescription: >\n  Use this when debugging duplicate requests, stale cache behavior, broad\n  subscriptions, selector churn, serializability warnings, or other Redux\n  Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector ->\n  render debugging loop plus RTK Query cache interpretation.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md'\n  - 'reduxjs/redux-toolkit:docs/usage/usage-guide.md'\n---\n\n# Debug Redux Toolkit Apps\n\n## Setup\n\n```ts\nimport { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as Post[],\n    status: 'idle' as 'idle' | 'pending' | 'succeeded' | 'failed',\n  },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchPosts.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(fetchPosts.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.items = action.payload\n      })\n  },\n})\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n```\n\n## Core Patterns\n\n### Debug in order: action -> reducer -> selector -> render\n\n```ts\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectPostsStatus = (state: RootState) => state.posts.status\n\nstore.dispatch(fetchPosts())\n\nconsole.log(selectPostsStatus(store.getState()))\nconsole.log(selectPosts(store.getState()))\n```\n\nIf a component looks wrong, first verify the action fired, then the reducer state, then the selector result, then the render boundary.\n\n### Narrow subscriptions at the usage site\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{posts.length}</div>\n    </div>\n  )\n}\n```\n\nReact-Redux behaves best when components select only the values they render and do it as close to usage as possible.\n\n### Interpret RTK Query invalidation correctly\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf invalidation did not visibly refetch, check whether anything was still subscribed to that cache entry.\n\n## Common Mistakes\n\n### HIGH Dispatching fetch thunks from effects without a thunk-level guard\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const dispatch = useAppDispatch()\n  const postStatus = useAppSelector((state) => state.posts.status)\n\n  useEffect(() => {\n    if (postStatus === 'idle') {\n      dispatch(fetchPosts())\n    }\n  }, [dispatch, postStatus])\n\n  return null\n}\n```\n\nCorrect:\n\n```ts\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n```\n\nReact StrictMode can run effects twice in development, so the guard belongs in the thunk as well as the component.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### HIGH Ignoring serializable-state warnings\n\nWrong:\n\n```ts\nconst initialState = {\n  lastSeen: new Date(),\n  pendingIds: new Set<string>(),\n}\n```\n\nCorrect:\n\n```ts\nconst initialState = {\n  lastSeenIso: new Date().toISOString(),\n  pendingIds: [] as string[],\n}\n```\n\nNon-serializable values break DevTools, replay, persistence, and equality assumptions in subtle ways.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Selecting broad state in parents and threading props\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const postsState = useAppSelector((state) => state.posts)\n  return <PostsList items={postsState.items} status={postsState.status} />\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsList() {\n  const items = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{items.length}</div>\n    </div>\n  )\n}\n```\n\nSelecting whole slices high in the tree widens the subscription surface and pushes rerenders through props.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Returning unstable objects from query selection logic\n\nWrong:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: [...data],\n  }),\n})\n```\n\nCorrect:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: data,\n  }),\n})\n```\n\nNew object and array references defeat memoization and make components rerender even when the underlying cached data did not change.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md\n","packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md":"---\nname: evolve-and-diagnose-redux-apps/migrate-to-modern-redux\ndescription: >\n  Use this when moving a legacy Redux codebase to current RTK patterns. Covers\n  replacing createStore with configureStore, migrating touched reducers to\n  createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks\n  with RTK Query instead of writing new legacy Redux code.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux-toolkit:packages/rtk-codemods/README.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Migrate To Modern Redux\n\n## Setup\n\n```ts\n// before\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nconst rootReducer = combineReducers({\n  posts: postsReducer,\n  users: usersReducer,\n})\n\nexport const legacyStore = createStore(rootReducer, applyMiddleware(thunk))\n\n// after\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n```\n\n## Core Patterns\n\n### Replace the store setup first\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nThis is the one migration step that can happen immediately while old reducers continue to work.\n\n### Migrate reducers as you touch them\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype TodosState = {\n  items: { id: string; text: string; completed: boolean }[]\n}\n\nconst initialState: TodosState = {\n  items: [],\n}\n\nexport const todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.items.push({ ...action.payload, completed: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.items.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.completed = !todo.completed\n      }\n    },\n  },\n})\n```\n\nOnce a reducer needs editing, migrate that reducer instead of adding more legacy code to it.\n\n### Use codemods for mechanical updates\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder src/features/posts/postsSlice.ts\nnpx @reduxjs/rtk-codemods createReducerBuilder src/features/posts/postsReducer.ts\n```\n\nUse codemods for repetitive RTK API migrations, then review the result and finish the semantic cleanup by hand.\n\n### Replace legacy server-data stacks with RTK Query\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nWhen the old code is just request status plus fetched data, migrate toward RTK Query instead of carrying the thunk stack forward forever.\n\n## Common Mistakes\n\n### HIGH Attempting a big-bang rewrite\n\nWrong:\n\n```ts\n// Replace every reducer, every connected component, and every async flow\n// in one branch before shipping anything.\n```\n\nCorrect:\n\n```ts\n// 1) Switch createStore to configureStore\n// 2) Migrate one touched reducer to createSlice\n// 3) Convert touched connected components to hooks\n// 4) Repeat without introducing new legacy Redux code\n```\n\nModern Redux migration is incremental, but once the store is modernized new work should stop adding legacy patterns.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### CRITICAL Carrying removed RTK 2 config forms forward\n\nWrong:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: [logger],\n})\n```\n\nCorrect:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),\n})\n```\n\nRTK 2 removed array middleware configuration and other older builder forms that agents trained on RTK 1.x still emit.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Preserving hand-written fetch state by default\n\nWrong:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\ntype Todo = { id: string; text: string }\n\nconst initialState = {\n  items: [] as Todo[],\n  status: 'idle' as 'idle' | 'pending' | 'failed',\n}\n\nexport const fetchTodos = createAsyncThunk('todos/fetch', async () => {\n  const response = await fetch('/api/todos')\n  return (await response.json()) as Todo[]\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nIf the feature is really server cache, keep the migration moving toward RTK Query instead of rebuilding the old loading-flag architecture in new APIs.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n","packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md":"---\nname: manage-server-data/adopt-rtk-query\ndescription: >\n  Use this when adding RTK Query as the default server-data and document-cache\n  layer. Covers createApi, store integration, hooks, invalidation behavior,\n  optimistic updates, and deciding when RTK Query is the right cache model.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Adopt RTK Query\n\n## Setup\n\n```tsx\n// file: src/services/api.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    addPost: build.mutation<Post, Pick<Post, 'title'>>({\n      query: (body) => ({\n        url: 'posts',\n        method: 'POST',\n        body,\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n\nexport const { useGetPostsQuery, useAddPostMutation } = api\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { api } from '../services/api'\n\nexport const store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n\n// file: src/App.tsx\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { useAddPostMutation, useGetPostsQuery } from './services/api'\n\nfunction Posts() {\n  const { data: posts = [] } = useGetPostsQuery()\n  const [addPost] = useAddPostMutation()\n\n  return (\n    <div>\n      <button onClick={() => addPost({ title: 'Write docs' })}>Add</button>\n      <ul>\n        {posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nexport function App() {\n  return (\n    <Provider store={store}>\n      <Posts />\n    </Provider>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep one API slice per base URL and extend it\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n\nexport const postsApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getPosts: build.query<{ id: string; title: string }[], void>({\n      query: () => 'posts',\n    }),\n  }),\n})\n```\n\nSplit files with `injectEndpoints`, not by making multiple `createApi` roots for the same backend.\n\n### Use tags for cache invalidation\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: (_result, _error, { id }) => [{ type: 'Post', id }],\n    }),\n  }),\n})\n```\n\nTreat tags as the normal invalidation path before reaching for manual cache patching.\n\n### Do optimistic updates in endpoint lifecycles\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n        const patch = dispatch(\n          api.util.updateQueryData('getPosts', undefined, (draft) => {\n            const post = draft.find((item) => item.id === id)\n            if (post) {\n              post.title = title\n            }\n          }),\n        )\n\n        try {\n          await queryFulfilled\n        } catch {\n          patch.undo()\n        }\n      },\n    }),\n  }),\n})\n```\n\nKeep optimistic and pessimistic cache updates inside endpoint lifecycle handlers so they stay coupled to the request.\n\n## Common Mistakes\n\n### CRITICAL Creating multiple API slices for one backend\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst postsApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getUsers: build.query<User[], void>({ query: () => 'users' }),\n  }),\n})\n```\n\nOne API slice per base URL preserves invalidation behavior and avoids duplicated middleware work.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx\n\n### HIGH Forgetting `api.reducer` or `api.middleware`\n\nWrong:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n```\n\nRTK Query hooks need both the reducer and middleware to manage cache state and request lifecycles.\n\nSource: reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx\n\n### MEDIUM Persisting browser API cache by default\n\nWrong:\n\n```ts\nconst storage = window.localStorage\n\nconst persistConfig = {\n  key: 'root',\n  storage,\n}\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n```\n\nPersisting RTK Query cache in browsers often keeps stale data around longer than users expect; treat persistence as a special case, not the default.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx\n\n### HIGH Patching cache from components\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst dispatch = useAppDispatch()\n\nuseEffect(() => {\n  dispatch(\n    api.util.updateQueryData('getPosts', undefined, (draft) => {\n      draft.push({ id: 'p3', title: 'Patched from component' })\n    }),\n  )\n}, [dispatch])\n```\n\nCorrect:\n\n```ts\nupdatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n  query: ({ id, title }) => ({\n    url: `posts/${id}`,\n    method: 'PATCH',\n    body: { title },\n  }),\n  async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n    const patch = dispatch(\n      api.util.updateQueryData('getPosts', undefined, (draft) => {\n        const post = draft.find((item) => item.id === id)\n        if (post) {\n          post.title = title\n        }\n      }),\n    )\n\n    try {\n      await queryFulfilled\n    } catch {\n      patch.undo()\n    }\n  },\n})\n```\n\nComponent-level cache patches drift away from the mutation lifecycle that should own them.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx\n\n### HIGH Expecting invalidation to refetch unsubscribed queries\n\nWrong:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nconst subscription = store.dispatch(api.endpoints.getPosts.initiate())\nsubscription.unsubscribe()\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nCorrect:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nstore.dispatch(api.endpoints.getPosts.initiate())\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nInvalidation only refetches actively subscribed queries; if no component is using that cache entry, RTK Query drops it and fetches again next time it is needed.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx\n\n## References\n\n- [Endpoint lifecycle details and cache tradeoffs](references/endpoint-lifecycle.md)\n","packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md":"# Endpoint Lifecycle\n\n## Invalidation rule\n\nWhen a mutation invalidates tags:\n\n- active subscribers refetch\n- inactive cache entries are removed\n- removed entries fetch again only when something subscribes later\n\nThat behavior is deliberate; invalidation is not a background \"refresh everything\" switch.\n\n## Document cache tradeoff\n\nRTK Query is a document cache, not a normalized entity graph cache.\n\nUse RTK Query by default when:\n\n- the data comes from request/response APIs\n- document caching is acceptable\n- tag invalidation and endpoint lifecycles solve the problem\n\nReach for a different tool when:\n\n- the real requirement is a normalized graph cache\n- the stack already has a domain-specific normalized client that fits better\n\nIf normalized caching is mandatory and no better library is already in the stack, a slice plus thunk flow may be the fallback.\n\n## Useful endpoint options\n\n- `providesTags`: tell RTK Query what cache entries this query represents\n- `invalidatesTags`: tell RTK Query what a mutation dirties\n- `onQueryStarted`: optimistic and pessimistic updates tied to a request\n- `onCacheEntryAdded`: long-lived subscriptions such as streaming data\n- `keepUnusedDataFor`: how long inactive cache entries stay around\n","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md":"---\nname: model-redux-state/build-slices-and-selectors\ndescription: >\n  Use this when authoring or refactoring slices with createSlice, selectors,\n  create.asyncThunk, entity adapters, or lazy reducer injection. Covers\n  Immer-backed mutation syntax, slice selectors, getSelectors, injectInto,\n  withLazyLoadedSlices, and current RTK 2 slice patterns.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - model-redux-state/design-state-ownership\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createSlice.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/combineSlices.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/immer-reducers.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Build Slices And Selectors\n\n## Setup\n\n```ts\n// file: src/app/createAppSlice.ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nexport const createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\n// file: src/features/posts/postsSlice.ts\nimport { createSelector } from '@reduxjs/toolkit'\nimport { createAppSlice } from '../../app/createAppSlice'\n\ntype PostsState = {\n  items: { id: string; title: string; published: boolean }[]\n  status: 'idle' | 'pending' | 'succeeded' | 'failed'\n}\n\nconst initialState: PostsState = {\n  items: [],\n  status: 'idle',\n}\n\nexport const postsSlice = createAppSlice({\n  name: 'posts',\n  initialState,\n  reducers: (create) => ({\n    postAdded: create.reducer<{ id: string; title: string }>(\n      (state, action) => {\n        state.items.push({ ...action.payload, published: false })\n      },\n    ),\n    fetchPosts: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/posts')\n        return (await response.json()) as {\n          id: string\n          title: string\n          published: boolean\n        }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'succeeded'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n  selectors: {\n    selectPosts: (state) => state.items,\n    selectPublishedPosts: createSelector(\n      [(state: PostsState) => state.items],\n      (items) => items.filter((post) => post.published),\n    ),\n  },\n})\n\nexport const { postAdded, fetchPosts } = postsSlice.actions\nexport const { selectPosts, selectPublishedPosts } = postsSlice.selectors\n```\n\n## Core Patterns\n\n### Use mutating logic inside slice reducers\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string; text: string; done: boolean }[],\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.push({ ...action.payload, done: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.done = !todo.done\n      }\n    },\n  },\n})\n```\n\nImmer is the default inside `createSlice`; write the reducer logic directly instead of copying arrays and objects by hand.\n\n### Define selectors in the slice when they belong to the slice\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n  selectors: {\n    selectValue: (state) => state.value,\n    selectIsPositive: (state) => state.value > 0,\n  },\n})\n\nconst { selectValue, selectIsPositive } = counterSlice.selectors\n```\n\nSlice selectors keep state-location knowledge next to the slice.\n\n### Use `create.asyncThunk` when the async lifecycle belongs to the slice\n\n```ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nconst createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\nconst usersSlice = createAppSlice({\n  name: 'users',\n  initialState: {\n    items: [] as { id: string; name: string }[],\n    status: 'idle' as 'idle' | 'pending' | 'failed',\n  },\n  reducers: (create) => ({\n    fetchUsers: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/users')\n        return (await response.json()) as { id: string; name: string }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'idle'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n})\n```\n\nUse this when the async lifecycle handlers naturally live with the slice; otherwise regular `createAsyncThunk` is still fine.\n\n### Use entity adapters and lazy injection for scalable slices\n\n```ts\nimport {\n  combineSlices,\n  createEntityAdapter,\n  createSlice,\n} from '@reduxjs/toolkit'\n\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n\nconst booksSlice = createSlice({\n  name: 'books',\n  initialState: booksAdapter.getInitialState(),\n  reducers: {\n    booksReceived: booksAdapter.setAll,\n  },\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices().withLazyLoadedSlices<LazyLoadedSlices>()\n\ndeclare module './rootReducer' {\n  export interface LazyLoadedSlices {}\n}\n\nconst injectedBooksSlice = booksSlice.injectInto(rootReducer)\n\nconst selectors = booksAdapter.getSelectors(\n  (state: ReturnType<typeof rootReducer.selector.original>) =>\n    injectedBooksSlice.selectSlice(state),\n)\n```\n\nEntity adapters standardize normalized collections, and `injectInto` lets a slice stay aware of its injected location.\n\n## Common Mistakes\n\n### CRITICAL Using mutating logic outside slice reducers\n\nWrong:\n\n```ts\ntype Todo = { id: string; text: string }\n\nexport function addTodo(todos: Todo[], todo: Todo) {\n  todos.push(todo)\n  return todos\n}\n```\n\nCorrect:\n\n```ts\ntype Todo = { id: string; text: string }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as Todo[],\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nMutation syntax is only safe inside Immer-backed reducer contexts such as `createSlice` and `createReducer`.\n\nSource: reduxjs/redux-toolkit:docs/usage/immer-reducers.md\n\n### HIGH Writing hand-written switch reducers as the default\n\nWrong:\n\n```ts\nexport default function todosReducer(\n  state = initialState,\n  action: { type: string; payload?: Todo },\n) {\n  switch (action.type) {\n    case 'todos/todoAdded':\n      return state.concat(action.payload as Todo)\n    default:\n      return state\n  }\n}\n```\n\nCorrect:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nHand-written reducers are an escape hatch for proven bottlenecks, not the normal thing an agent should generate in RTK code.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Writing RTK 1.x object syntax for `extraReducers`\n\nWrong:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: {\n    [fetchPosts.fulfilled.type]: (state, action) => {\n      state.items = action.payload\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: (builder) => {\n    builder.addCase(fetchPosts.fulfilled, (state, action) => {\n      state.items = action.payload\n    })\n  },\n})\n```\n\nRTK 2 removed the object form; agents trained on RTK 1.x still generate it.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Assuming `entity.id` exists for every collection\n\nWrong:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>()\n```\n\nCorrect:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n```\n\nAdapters default to `entity.id`; collections keyed by another field must provide `selectId`.\n\nSource: reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx\n\n## References\n\n- [Slice selectors, async creators, and lazy injection details](references/slice-patterns.md)\n","packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md":"# Slice Patterns\n\n## `getSelectors` for alternate mounting points\n\n```ts\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {},\n  selectors: {\n    selectValue: (state) => state.value,\n  },\n})\n\nexport const { selectValue } = counterSlice.getSelectors(\n  (state: { customCounter: { value: number } }) => state.customCounter,\n)\n```\n\nUse `getSelectors` when the slice is not mounted at its default `reducerPath`.\n\n## `withLazyLoadedSlices` and `injectInto`\n\n```ts\nimport { combineSlices, createSlice, type WithSlice } from '@reduxjs/toolkit'\n\nconst staticSlice = createSlice({\n  name: 'static',\n  initialState: { ready: true },\n  reducers: {},\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices(staticSlice).withLazyLoadedSlices<LazyLoadedSlices>()\n\nconst lazySlice = createSlice({\n  name: 'lazy',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\ndeclare module '../rootReducer' {\n  export interface LazyLoadedSlices extends WithSlice<typeof lazySlice> {}\n}\n\nexport const injectedLazySlice = lazySlice.injectInto(rootReducer)\n```\n\nThis keeps RootState types aware of reducers that will be injected later.\n\n## Selector caveat\n\nIf a selector depends on caller-specific arguments and must be memoized per caller, prefer a selector factory outside `createSlice.selectors`. `createSlice.selectors` gives you one selector instance, not a selector factory.\n","packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md":"---\nname: model-redux-state/design-state-ownership\ndescription: >\n  Use this when deciding whether data belongs in Redux, component state, router\n  state, or another external source. Covers state ownership, authority\n  boundaries, slice sizing, and when to move or split data as the app evolves.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md'\n---\n\n# Design State Ownership\n\n## Setup\n\n```tsx\nimport { useState } from 'react'\nimport { createSlice } from '@reduxjs/toolkit'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string; content: string }[],\n  reducers: {\n    postAdded(\n      state,\n      action: { payload: { id: string; title: string; content: string } },\n    ) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst { postAdded } = postsSlice.actions\n\nexport function AddPostForm() {\n  const [title, setTitle] = useState('')\n  const [content, setContent] = useState('')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(postAdded({ id: 'p1', title, content }))\n      }}\n    >\n      <input value={title} onChange={(event) => setTitle(event.target.value)} />\n      <textarea\n        value={content}\n        onChange={(event) => setContent(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep editable form state local until the user commits it\n\n```tsx\nimport { useState } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\nimport { profileSaved } from './profileSlice'\n\nexport function ProfileForm() {\n  const [displayName, setDisplayName] = useState('Lenz')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(profileSaved({ displayName }))\n      }}\n    >\n      <input\n        value={displayName}\n        onChange={(event) => setDisplayName(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\nPrefer Redux for shared, durable app state, not every keystroke.\n\n### Keep URL state with the router and combine it at the edge\n\n```tsx\nimport { createSelector } from '@reduxjs/toolkit'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppSelector } from '../../app/hooks'\n\ntype RootState = {\n  posts: {\n    items: { id: string; title: string; published: boolean }[]\n  }\n}\n\nconst selectPosts = (state: RootState) => state.posts.items\n\nconst selectVisiblePosts = createSelector(\n  [selectPosts, (_state: RootState, filter: string) => filter],\n  (posts, filter) =>\n    filter === 'published' ? posts.filter((post) => post.published) : posts,\n)\n\nexport function PostsList() {\n  const [searchParams] = useSearchParams()\n  const filter = searchParams.get('filter') ?? 'all'\n  const posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n\n  return <div>{posts.length}</div>\n}\n```\n\nIf the router already owns a piece of state, pass it into selectors or combine it in the component instead of syncing it into Redux.\n\n### Re-size slices when access patterns change\n\n```ts\nimport { combineReducers, createSlice } from '@reduxjs/toolkit'\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: { items: [] as { id: string; title: string }[] },\n  reducers: {},\n})\n\nexport const rootReducer = combineReducers({\n  auth: authSlice.reducer,\n  posts: postsSlice.reducer,\n})\n```\n\nRevisit slice size over time; unrelated data should split apart, and data constantly stitched together in every component may belong closer together.\n\n## Common Mistakes\n\n### MEDIUM Putting form editing state in Redux\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nconst selectDraftTitle = (state: { draft: { title: string } }) =>\n  state.draft.title\n\nconst title = useAppSelector(selectDraftTitle)\n```\n\nCorrect:\n\n```tsx\nconst [title, setTitle] = useState('')\n\n<input value={title} onChange={(event) => setTitle(event.target.value)} />\n```\n\nPer-keystroke dispatching adds global complexity for data that usually lives in one component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Synchronizing router or URL state into Redux\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppDispatch } from '../../app/hooks'\n\nfunction PostsPage() {\n  const [searchParams] = useSearchParams()\n  const dispatch = useAppDispatch()\n\n  useEffect(() => {\n    dispatch(filterChanged(searchParams.get('filter') ?? 'all'))\n  }, [dispatch, searchParams])\n\n  return null\n}\n```\n\nCorrect:\n\n```tsx\nconst filter = searchParams.get('filter') ?? 'all'\nconst posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n```\n\nURL state already has an authoritative owner; duplicating it into Redux creates two sources of truth.\n\nSource: maintainer interview\n\n### HIGH Naming state after components\n\nWrong:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst loginReducer = (state = { open: false }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  loginScreen: loginReducer,\n  postsList: postsReducer,\n})\n```\n\nCorrect:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst authReducer = (state = { userId: null as string | null }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  auth: authReducer,\n  posts: postsReducer,\n})\n```\n\nStore keys should describe data or domain concepts, not the current component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Letting slice boundaries fossilize\n\nWrong:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\ntype AppNotification = { id: string; message: string }\n\nconst appSlice = createSlice({\n  name: 'app',\n  initialState: {\n    auth: { userId: null as string | null },\n    posts: [] as Post[],\n    notifications: [] as AppNotification[],\n  },\n  reducers: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as Post[],\n  reducers: {},\n})\n```\n\nWhen unrelated data is welded together, every change point gets noisier; split or merge slices as actual access patterns demand.\n\nSource: maintainer interview\n\n### HIGH Blindly spreading payloads into state\n\nWrong:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  return { ...state, ...action.payload }\n}\n```\n\nCorrect:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  state.id = action.payload.id\n  state.name = action.payload.name\n}\n```\n\nReducers should own the slice shape instead of treating payloads as trusted state patches.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n## References\n\n- [State ownership heuristics](references/state-ownership.md)\n","packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md":"# State Ownership Heuristics\n\n## Choose the owner, then the tool\n\n| Kind of state                              | Default owner   | Typical tool                             |\n| ------------------------------------------ | --------------- | ---------------------------------------- |\n| Editable form fields                       | Component       | `useState`                               |\n| Shared mutable app data                    | Redux           | Slice state                              |\n| Server cache                               | RTK Query       | `createApi`                              |\n| URL, pathname, search params               | Router          | Router APIs plus selector inputs         |\n| Browser-only authority like `localStorage` | External source | Read at boundaries, then dispatch events |\n\n## Good reasons to move data into Redux\n\n- Multiple distant parts of the UI need the same mutable data.\n- You need time-travel debugging or a stable action history.\n- The reducer should own transitions because they mix old store state with new inputs.\n\n## Reasons to keep data out of Redux\n\n- Another system already owns it, such as the router.\n- It only matters during editing inside one component tree.\n- It is server cache and RTK Query fits the use case better.\n\n## Re-evaluate slice size\n\n- If data is constantly stitched together outside reducers, it may belong closer together.\n- If unrelated updates keep touching the same slice, split the slice by domain ownership.\n","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md":"---\nname: orchestrate-side-effects/handle-side-effects\ndescription: >\n  Use this when choosing between RTK Query, createAsyncThunk, handwritten\n  thunks, and createListenerMiddleware. Covers imperative versus reactive\n  workflows, listener middleware setup, and keeping side effects out of\n  reducers and UI components.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n---\n\n# Handle Side Effects\n\n## Setup\n\n```ts\nimport {\n  configureStore,\n  createListenerMiddleware,\n  createSlice,\n} from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveStarted(state) {\n      state.status = 'idle'\n    },\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nexport const store = configureStore({\n  reducer: {\n    docs: docsSlice.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\nexport const startAppListening = listenerMiddleware.startListening.withTypes<\n  RootState,\n  AppDispatch\n>()\n```\n\n## Core Patterns\n\n### Use RTK Query for server cache by default\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf the problem is server data that should be cached and re-used, start with RTK Query instead of a thunk.\n\n### Use `createAsyncThunk` for imperative workflows\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Draft = { title: string }\n\nexport const draftSaved = createAsyncThunk(\n  'drafts/save',\n  async (draft: Draft) => {\n    const response = await fetch('/api/drafts', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(draft),\n    })\n\n    return (await response.json()) as { id: string; title: string }\n  },\n)\n\nconst draftsSlice = createSlice({\n  name: 'drafts',\n  initialState: { status: 'idle' as 'idle' | 'pending' | 'failed' },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(draftSaved.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(draftSaved.fulfilled, (state) => {\n        state.status = 'idle'\n      })\n      .addCase(draftSaved.rejected, (state) => {\n        state.status = 'failed'\n      })\n  },\n})\n```\n\nUse a thunk when you need one imperative async workflow with `dispatch` and `getState`.\n\n### Use listener middleware for reactive workflows\n\n```ts\nimport { createListenerMiddleware, createSlice } from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst notificationsSlice = createSlice({\n  name: 'notifications',\n  initialState: [] as string[],\n  reducers: {\n    notificationQueued(state, action: { payload: string }) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nlistenerMiddleware.startListening({\n  actionCreator: docsSlice.actions.saveFinished,\n  effect: async (_action, listenerApi) => {\n    listenerApi.dispatch(\n      notificationsSlice.actions.notificationQueued('Document saved'),\n    )\n  },\n})\n```\n\nListeners fit workflows that react to future actions or state changes over time instead of driving one imperative request from a single callsite.\n\n## Common Mistakes\n\n### CRITICAL Running side effects inside reducers\n\nWrong:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string }[],\n  reducers: {\n    todoSaved(state, action: { payload: { id: string } }) {\n      fetch('/api/todos', { method: 'POST' })\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\nconst todoSaved = createAsyncThunk(\n  'todos/save',\n  async (todo: { id: string }) => {\n    await fetch('/api/todos', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(todo),\n    })\n    return todo\n  },\n)\n```\n\nReducers must stay pure even when Immer is available.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using thunks to watch future state changes\n\nWrong:\n\n```ts\nexport const waitForSave =\n  () =>\n  async (_dispatch: unknown, getState: () => { docs: { status: string } }) => {\n    while (getState().docs.status !== 'saved') {\n      await new Promise((resolve) => setTimeout(resolve, 100))\n    }\n  }\n```\n\nCorrect:\n\n```ts\nstartAppListening({\n  predicate: (_action, currentState) => currentState.docs.status === 'saved',\n  effect: async () => {\n    console.log('Document saved')\n  },\n})\n```\n\nPolling inside thunks fights the architecture; listener middleware is the reactive tool.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n### HIGH Appending listener middleware after the default checks\n\nWrong:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(listenerMiddleware.middleware),\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n```\n\nListener add and remove actions may carry functions, so the listener middleware needs to run before serializability checks.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n## References\n\n- [Listener helpers and decision heuristics](references/listener-workflows.md)\n","packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md":"# Listener Workflows\n\n## Decision table\n\n| Need                                                       | Reach for                     |\n| ---------------------------------------------------------- | ----------------------------- |\n| Cached server data                                         | RTK Query                     |\n| One imperative async workflow with `dispatch` / `getState` | `createAsyncThunk` or a thunk |\n| React to later actions or state transitions                | `createListenerMiddleware`    |\n\nA good app often mixes imperative and reactive workflows. The split is by job, not by ideology.\n\n## Useful listener helpers\n\n- `predicate`: react to any action when a state condition becomes true\n- `condition`: wait until a condition becomes true before continuing\n- `take`: wait for the next matching action\n- `cancelActiveListeners`: cancel older instances of the same workflow\n- `fork`: start a child task\n\n## Example: cancel stale work\n\n```ts\nstartAppListening({\n  actionCreator: searchRequested,\n  effect: async (action, listenerApi) => {\n    listenerApi.cancelActiveListeners()\n    await listenerApi.delay(250)\n    listenerApi.dispatch(searchStarted(action.payload))\n  },\n})\n```\n\nThis is the kind of long-lived reactive behavior that does not fit a thunk well.\n"},"items":[{"name":"codegen-overrides.md","path":"packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Codegen Overrides\n\n## Common override targets\n\n| Need                                            | Override                           |\n| ----------------------------------------------- | ---------------------------------- |\n| Query should really be a mutation               | `type: 'mutation'`                 |\n| Header or optional parameters should be omitted | `parameterFilter`                  |\n| Generated tags are too coarse                   | `providesTags` / `invalidatesTags` |\n\n## Example: split one schema into multiple outputs\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFiles: {\n    './src/store/userApi.ts': {\n      exportName: 'userApi',\n      filterEndpoints: [/user/i],\n      hooks: true,\n    },\n    './src/store/orderApi.ts': {\n      exportName: 'orderApi',\n      filterEndpoints: [/order/i],\n      hooks: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Review checklist\n\n- Does every generated endpoint belong in the shared API root?\n- Did any query or mutation type need correction?\n- Are generated tags specific enough for real invalidation needs?\n- Did the schema force parameters into the signature that the app should hide?\n","isInternal":false,"tokens":274,"sizeBytes":1323},{"name":"SKILL.md","path":"packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md","title":"manage-server-data/generate-rtk-query-from-openapi","category":"anthropic-skill","format":"markdown","content":"---\nname: manage-server-data/generate-rtk-query-from-openapi\ndescription: >\n  Use this when generating RTK Query endpoints from OpenAPI schemas with\n  @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints,\n  endpointOverrides, generated tags, and reviewing generated output before it\n  becomes part of the app.\ntype: composition\nlibrary: '@rtk-query/codegen-openapi'\nlibrary_version: '2.2.0'\nrequires:\n  - manage-server-data/adopt-rtk-query\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/types.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/generate.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/README.md'\n---\n\n# Generate RTK Query From OpenAPI\n\n## Setup\n\n```ts\n// file: src/store/emptyApi.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n\n// file: openapi-config.ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n};\n\nexport default config;\n```\n\nRun:\n\n```bash\nnpx @rtk-query/codegen-openapi openapi-config.ts\n```\n\n## Core Patterns\n\n### Generate into an empty shared API\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nCodegen works best when it extends a single RTK Query architecture instead of creating standalone API roots.\n\n### Filter endpoints when the schema is too broad\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/userApi.ts',\n  exportName: 'userApi',\n  hooks: true,\n  filterEndpoints: ['loginUser', /User/],\n};\n\nexport default config;\n```\n\nStart with a narrow slice of the schema if the full surface area is too noisy or the package boundaries differ from the OpenAPI file.\n\n### Use `endpointOverrides` to fix generation results\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n    {\n      pattern: /.*/,\n      parameterFilter: (_name, parameter) => parameter.in !== 'header',\n    },\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n\nexport default config;\n```\n\nReview generated endpoints and override type, parameter, or tag behavior instead of hand-editing the emitted file.\n\n## Common Mistakes\n\n### HIGH Assuming generated tags are already specific enough\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n```\n\nGenerated tags are string-only by default, so they can invalidate more cache than intended.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### HIGH Generating a brand-new API root instead of extending an empty one\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\ntype Pet = { id: string; name: string };\n\nexport const petApi = createApi({\n  reducerPath: 'petApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getPetById: build.query<Pet, string>({\n      query: (id) => `pets/${id}`,\n    }),\n  }),\n});\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nGenerated code should plug into one RTK Query architecture so invalidation and store wiring stay coherent.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### MEDIUM Trusting generated shapes without overrides or review\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n  ],\n};\n```\n\nReal schemas often need type, parameter, or tag correction; treat generated output as reviewed source, not gospel.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n## References\n\n- [Codegen override patterns and review checklist](references/codegen-overrides.md)\n","frontmatter":{"name":"manage-server-data/generate-rtk-query-from-openapi","description":"Use this when generating RTK Query endpoints from OpenAPI schemas with @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints, endpointOverrides, generated tags, and reviewing generated output before it becomes part of the app.\n","type":"composition","library":"@rtk-query/codegen-openapi","library_version":"2.2.0","requires":["manage-server-data/adopt-rtk-query"],"sources":["reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx","reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/types.ts","reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/generate.ts","reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/README.md"]},"isInternal":false,"tokens":1504,"sizeBytes":5754},{"name":"store-lifetime.md","path":"packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Store Lifetime\n\n## Decision table\n\n| Environment                | Default store shape                     | Why                                                                                                  |\n| -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |\n| Client-only SPA            | One module-level singleton store        | There is one browser session and no cross-request leakage risk.                                      |\n| SSR-heavy React app        | `makeStore()` plus provider-local state | Each request needs its own store instance, but that instance must stay stable across client renders. |\n| Non-React integration code | Direct store access can be acceptable   | This is outside the React context boundary and should stay out of UI components.                     |\n\n## SPA pattern\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n```\n\nUse this for classic browser SPAs.\n\n## SSR-heavy React pattern\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      posts: postsSlice.reducer,\n    },\n  })\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCreate the store per request, then keep it stable inside the provider component.\n","isInternal":false,"tokens":355,"sizeBytes":1895},{"name":"SKILL.md","path":"packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md","title":"build-modern-redux-apps/modern-redux","category":"anthropic-skill","format":"markdown","content":"---\nname: build-modern-redux-apps/modern-redux\ndescription: >\n  Use this when setting up a new Redux Toolkit app or modernizing an existing\n  React + Redux codebase. Covers configureStore, Provider wiring, typed hooks,\n  hooks-first React-Redux usage, feature folders, and the correct store lifetime\n  for SPA and SSR-heavy React environments.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/typescript.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/nextjs.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Modern Redux\n\n## Setup\n\n```tsx\n// file: src/features/counter/counterSlice.ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nexport const counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\nexport const { increment } = counterSlice.actions\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterSlice.reducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\n// file: src/app/hooks.ts\nimport { useDispatch, useSelector } from 'react-redux'\nimport type { AppDispatch, RootState } from './store'\n\nexport const useAppDispatch = useDispatch.withTypes<AppDispatch>()\nexport const useAppSelector = useSelector.withTypes<RootState>()\n\n// file: src/features/counter/Counter.tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n\n// file: src/main.tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { Counter } from './features/counter/Counter'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <Provider store={store}>\n    <Counter />\n  </Provider>,\n)\n```\n\n## Core Patterns\n\n### Keep React components on hooks, not wrappers\n\n```tsx\nimport { postAdded, selectPosts } from './postsSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector(selectPosts)\n  const dispatch = useAppDispatch()\n\n  return (\n    <button\n      onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}\n    >\n      {posts.length}\n    </button>\n  )\n}\n```\n\nHooks are the default React-Redux integration for new code.\n\n### Create the store inside the provider for SSR-heavy React apps\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      counter: counterSlice.reducer,\n    },\n  })\n\nexport type AppStore = ReturnType<typeof makeStore>\nexport type RootState = ReturnType<AppStore['getState']>\nexport type AppDispatch = AppStore['dispatch']\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nIn SSR-heavy React frameworks, create a store per request and keep that instance stable across renders.\n\n### Keep app wiring in `app/` and feature logic in feature folders\n\n```text\nsrc/\n  app/\n    store.ts\n    hooks.ts\n  features/\n    posts/\n      postsSlice.ts\n      PostsList.tsx\n    users/\n      usersSlice.ts\n      UsersList.tsx\n```\n\nThis keeps store wiring centralized and feature logic colocated.\n\n## Common Mistakes\n\n### HIGH Importing the store in React components\n\nWrong:\n\n```tsx\nimport { store } from '../../app/store'\n\nexport function PostsList() {\n  const posts = store.getState().posts\n  return <div>{posts.length}</div>\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts)\n  return <div>{posts.length}</div>\n}\n```\n\nReact components should read through context and hooks; direct store imports are a separate escape hatch for non-React integrations, not the default UI pattern.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Defaulting to `connect()` in new React code\n\nWrong:\n\n```tsx\nimport { connect } from 'react-redux'\n\nconst increment = () => ({ type: 'counter/increment' as const })\nconst mapState = (state: { counter: { value: number } }) => ({\n  value: state.counter.value,\n})\nconst mapDispatch = { increment }\n\nfunction Counter({\n  value,\n  increment,\n}: ReturnType<typeof mapState> & typeof mapDispatch) {\n  return <button onClick={() => increment()}>{value}</button>\n}\n\nexport default connect(mapState, mapDispatch)(Counter)\n```\n\nCorrect:\n\n```tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n```\n\nHooks are the modern default, simpler to type, and the maintainers explicitly want agents to steer new code away from `connect`.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Recreating the store during render in SSR-heavy apps\n\nWrong:\n\n```tsx\n'use client'\n\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const store = makeStore()\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCorrect:\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nA new store on every render loses client state, while a module singleton can leak across requests on the server.\n\nSource: reduxjs/redux-toolkit:docs/usage/nextjs.mdx\n\n### HIGH Keeping `createStore` boilerplate as the default\n\nWrong:\n\n```ts\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nconst rootReducer = combineReducers({\n  counter: counterReducer,\n})\n\nexport const store = createStore(rootReducer, applyMiddleware(thunk))\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer,\n  },\n})\n```\n\nManual store setup works, but it throws away RTK's default middleware, dev checks, and the current recommended baseline.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n## References\n\n- [Store lifetime and framework boundaries](references/store-lifetime.md)\n","frontmatter":{"name":"build-modern-redux-apps/modern-redux","description":"Use this when setting up a new Redux Toolkit app or modernizing an existing React + Redux codebase. Covers configureStore, Provider wiring, typed hooks, hooks-first React-Redux usage, feature folders, and the correct store lifetime for SPA and SSR-heavy React environments.\n","type":"lifecycle","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["build-modern-redux-apps/redux-dataflow"],"sources":["reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx","reduxjs/redux-toolkit:docs/tutorials/typescript.md","reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx","reduxjs/redux-toolkit:docs/usage/nextjs.mdx","reduxjs/redux:docs/style-guide/style-guide.md"]},"isInternal":false,"tokens":1805,"sizeBytes":7629},{"name":"SKILL.md","path":"packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md","title":"build-modern-redux-apps/redux-dataflow","category":"anthropic-skill","format":"markdown","content":"---\nname: build-modern-redux-apps/redux-dataflow\ndescription: >\n  Use this when you need the Redux event -> reducer -> selector -> render loop,\n  event-style actions, reducer-owned state transitions, derived data, or a\n  debugging model for Redux Toolkit apps.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Redux Dataflow\n\n## Setup\n\n```ts\nimport { configureStore, createSelector, createSlice } from '@reduxjs/toolkit'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as { id: string; title: string; published: boolean }[],\n    filter: 'all' as 'all' | 'published',\n  },\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.items.push({ ...action.payload, published: false })\n    },\n    postPublished(state, action: { payload: { id: string } }) {\n      const post = state.items.find((item) => item.id === action.payload.id)\n      if (post) {\n        post.published = true\n      }\n    },\n    filterChanged(state, action: { payload: 'all' | 'published' }) {\n      state.filter = action.payload\n    },\n  },\n})\n\nconst store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n\nconst selectPostsState = (state: RootState) => state.posts\nconst selectVisiblePosts = createSelector([selectPostsState], (postsState) =>\n  postsState.filter === 'all'\n    ? postsState.items\n    : postsState.items.filter((post) => post.published),\n)\n\nstore.dispatch(postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' }))\nstore.dispatch(postsSlice.actions.postPublished({ id: 'p1' }))\n\nconst visiblePosts = selectVisiblePosts(store.getState())\nconsole.log(visiblePosts)\n```\n\n## Core Patterns\n\n### Dispatch events, not setters\n\n```ts\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string }[],\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.push(action.payload)\n    },\n    postRemoved(state, action: { payload: { id: string } }) {\n      return state.filter((post) => post.id !== action.payload.id)\n    },\n    postUpdated(\n      state,\n      action: { payload: { id: string; changes: Partial<{ title: string }> } },\n    ) {\n      const post = state.find((item) => item.id === action.payload.id)\n      if (post && action.payload.changes.title) {\n        post.title = action.payload.changes.title\n      }\n    },\n  },\n})\n\npostsSlice.actions.postAdded({ id: 'p1', title: 'Draft' })\n```\n\nEvent-style actions explain what happened in the UI instead of hiding the transition behind a generic setter.\n\n### Let reducers combine old store data with new outside data\n\n```ts\nimport { createEntityAdapter, createSlice } from '@reduxjs/toolkit'\n\nconst postsAdapter = createEntityAdapter<{ id: string; title: string }>()\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: postsAdapter.getInitialState(),\n  reducers: {\n    postsReceived(state, action: { payload: { id: string; title: string }[] }) {\n      postsAdapter.upsertMany(state, action.payload)\n    },\n  },\n})\n\nconst incomingPosts = [\n  { id: 'p1', title: 'Draft' },\n  { id: 'p2', title: 'Published' },\n]\n\npostsSlice.actions.postsReceived(incomingPosts)\n```\n\nIf a transition mixes current store state with new external data, dispatch the new external data and let the reducer own the merge.\n\n### Derive values with selectors instead of storing duplicates\n\n```ts\nimport { createSelector } from '@reduxjs/toolkit'\n\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectFilter = (state: RootState) => state.posts.filter\n\nexport const selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nSelectors keep a single source of truth in state while still exposing the shapes the UI needs.\n\n## Common Mistakes\n\n### CRITICAL Mutating selected state outside reducers\n\nWrong:\n\n```ts\nconst post = selectPostById(store.getState(), 'p1')\n\nif (post) {\n  post.title = 'Changed in place'\n}\n```\n\nCorrect:\n\n```ts\nstore.dispatch(\n  postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),\n)\n```\n\nObjects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using setter-style actions instead of event-style actions\n\nWrong:\n\n```ts\nconst nextPosts = [\n  ...selectPosts(store.getState()),\n  { id: 'p2', title: 'Write docs' },\n]\nstore.dispatch(setPosts(nextPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postAdded({ id: 'p2', title: 'Write docs' }))\n```\n\nActions should describe events, not ask reducers to blindly replace state with a precomputed value.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Combining store state before dispatch\n\nWrong:\n\n```ts\nconst currentPosts = selectPosts(store.getState())\nconst mergedPosts = [\n  ...currentPosts.filter(\n    (currentPost) =>\n      !incomingPosts.some((incomingPost) => incomingPost.id === currentPost.id),\n  ),\n  ...incomingPosts,\n]\n\nstore.dispatch(postsReplaced(mergedPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postsReceived(incomingPosts))\n```\n\nIf the next state depends on current store state, the reducer should own that combination logic; only authoritative external snapshots should replace state wholesale.\n\nSource: maintainer interview\n\n### HIGH Ignoring current state in async reducers\n\nWrong:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  state.status = 'succeeded'\n  state.items = action.payload\n})\n```\n\nCorrect:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  if (state.status === 'pending') {\n    state.status = 'succeeded'\n    state.items = action.payload\n  }\n})\n```\n\nReducers that treat every lifecycle action as valid can move the slice into impossible states or let stale requests win.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### MEDIUM Storing derived values in state\n\nWrong:\n\n```ts\nconst initialState = {\n  items: [] as Post[],\n  visiblePosts: [] as Post[],\n}\n```\n\nCorrect:\n\n```ts\nconst selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nDerived values drift out of sync quickly; keep the raw state and derive the view shape.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n","frontmatter":{"name":"build-modern-redux-apps/redux-dataflow","description":"Use this when you need the Redux event -> reducer -> selector -> render loop, event-style actions, reducer-owned state transitions, derived data, or a debugging model for Redux Toolkit apps.\n","type":"core","library":"@reduxjs/toolkit","library_version":"2.11.2","sources":["reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md","reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md","reduxjs/redux:docs/style-guide/style-guide.md"]},"isInternal":false,"tokens":1618,"sizeBytes":6720},{"name":"SKILL.md","path":"packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md","title":"evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps","category":"anthropic-skill","format":"markdown","content":"---\nname: evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps\ndescription: >\n  Use this when debugging duplicate requests, stale cache behavior, broad\n  subscriptions, selector churn, serializability warnings, or other Redux\n  Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector ->\n  render debugging loop plus RTK Query cache interpretation.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md'\n  - 'reduxjs/redux-toolkit:docs/usage/usage-guide.md'\n---\n\n# Debug Redux Toolkit Apps\n\n## Setup\n\n```ts\nimport { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as Post[],\n    status: 'idle' as 'idle' | 'pending' | 'succeeded' | 'failed',\n  },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchPosts.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(fetchPosts.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.items = action.payload\n      })\n  },\n})\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n```\n\n## Core Patterns\n\n### Debug in order: action -> reducer -> selector -> render\n\n```ts\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectPostsStatus = (state: RootState) => state.posts.status\n\nstore.dispatch(fetchPosts())\n\nconsole.log(selectPostsStatus(store.getState()))\nconsole.log(selectPosts(store.getState()))\n```\n\nIf a component looks wrong, first verify the action fired, then the reducer state, then the selector result, then the render boundary.\n\n### Narrow subscriptions at the usage site\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{posts.length}</div>\n    </div>\n  )\n}\n```\n\nReact-Redux behaves best when components select only the values they render and do it as close to usage as possible.\n\n### Interpret RTK Query invalidation correctly\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf invalidation did not visibly refetch, check whether anything was still subscribed to that cache entry.\n\n## Common Mistakes\n\n### HIGH Dispatching fetch thunks from effects without a thunk-level guard\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const dispatch = useAppDispatch()\n  const postStatus = useAppSelector((state) => state.posts.status)\n\n  useEffect(() => {\n    if (postStatus === 'idle') {\n      dispatch(fetchPosts())\n    }\n  }, [dispatch, postStatus])\n\n  return null\n}\n```\n\nCorrect:\n\n```ts\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n```\n\nReact StrictMode can run effects twice in development, so the guard belongs in the thunk as well as the component.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### HIGH Ignoring serializable-state warnings\n\nWrong:\n\n```ts\nconst initialState = {\n  lastSeen: new Date(),\n  pendingIds: new Set<string>(),\n}\n```\n\nCorrect:\n\n```ts\nconst initialState = {\n  lastSeenIso: new Date().toISOString(),\n  pendingIds: [] as string[],\n}\n```\n\nNon-serializable values break DevTools, replay, persistence, and equality assumptions in subtle ways.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Selecting broad state in parents and threading props\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const postsState = useAppSelector((state) => state.posts)\n  return <PostsList items={postsState.items} status={postsState.status} />\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsList() {\n  const items = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{items.length}</div>\n    </div>\n  )\n}\n```\n\nSelecting whole slices high in the tree widens the subscription surface and pushes rerenders through props.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Returning unstable objects from query selection logic\n\nWrong:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: [...data],\n  }),\n})\n```\n\nCorrect:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: data,\n  }),\n})\n```\n\nNew object and array references defeat memoization and make components rerender even when the underlying cached data did not change.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md\n","frontmatter":{"name":"evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps","description":"Use this when debugging duplicate requests, stale cache behavior, broad subscriptions, selector churn, serializability warnings, or other Redux Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector -> render debugging loop plus RTK Query cache interpretation.\n","type":"lifecycle","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["build-modern-redux-apps/redux-dataflow"],"sources":["reduxjs/redux:docs/style-guide/style-guide.md","reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md","reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md","reduxjs/redux-toolkit:docs/usage/usage-guide.md"]},"isInternal":false,"tokens":1566,"sizeBytes":6427},{"name":"SKILL.md","path":"packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md","title":"evolve-and-diagnose-redux-apps/migrate-to-modern-redux","category":"anthropic-skill","format":"markdown","content":"---\nname: evolve-and-diagnose-redux-apps/migrate-to-modern-redux\ndescription: >\n  Use this when moving a legacy Redux codebase to current RTK patterns. Covers\n  replacing createStore with configureStore, migrating touched reducers to\n  createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks\n  with RTK Query instead of writing new legacy Redux code.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux-toolkit:packages/rtk-codemods/README.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Migrate To Modern Redux\n\n## Setup\n\n```ts\n// before\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nconst rootReducer = combineReducers({\n  posts: postsReducer,\n  users: usersReducer,\n})\n\nexport const legacyStore = createStore(rootReducer, applyMiddleware(thunk))\n\n// after\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n```\n\n## Core Patterns\n\n### Replace the store setup first\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nThis is the one migration step that can happen immediately while old reducers continue to work.\n\n### Migrate reducers as you touch them\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype TodosState = {\n  items: { id: string; text: string; completed: boolean }[]\n}\n\nconst initialState: TodosState = {\n  items: [],\n}\n\nexport const todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.items.push({ ...action.payload, completed: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.items.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.completed = !todo.completed\n      }\n    },\n  },\n})\n```\n\nOnce a reducer needs editing, migrate that reducer instead of adding more legacy code to it.\n\n### Use codemods for mechanical updates\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder src/features/posts/postsSlice.ts\nnpx @reduxjs/rtk-codemods createReducerBuilder src/features/posts/postsReducer.ts\n```\n\nUse codemods for repetitive RTK API migrations, then review the result and finish the semantic cleanup by hand.\n\n### Replace legacy server-data stacks with RTK Query\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nWhen the old code is just request status plus fetched data, migrate toward RTK Query instead of carrying the thunk stack forward forever.\n\n## Common Mistakes\n\n### HIGH Attempting a big-bang rewrite\n\nWrong:\n\n```ts\n// Replace every reducer, every connected component, and every async flow\n// in one branch before shipping anything.\n```\n\nCorrect:\n\n```ts\n// 1) Switch createStore to configureStore\n// 2) Migrate one touched reducer to createSlice\n// 3) Convert touched connected components to hooks\n// 4) Repeat without introducing new legacy Redux code\n```\n\nModern Redux migration is incremental, but once the store is modernized new work should stop adding legacy patterns.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### CRITICAL Carrying removed RTK 2 config forms forward\n\nWrong:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: [logger],\n})\n```\n\nCorrect:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),\n})\n```\n\nRTK 2 removed array middleware configuration and other older builder forms that agents trained on RTK 1.x still emit.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Preserving hand-written fetch state by default\n\nWrong:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\ntype Todo = { id: string; text: string }\n\nconst initialState = {\n  items: [] as Todo[],\n  status: 'idle' as 'idle' | 'pending' | 'failed',\n}\n\nexport const fetchTodos = createAsyncThunk('todos/fetch', async () => {\n  const response = await fetch('/api/todos')\n  return (await response.json()) as Todo[]\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nIf the feature is really server cache, keep the migration moving toward RTK Query instead of rebuilding the old loading-flag architecture in new APIs.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n","frontmatter":{"name":"evolve-and-diagnose-redux-apps/migrate-to-modern-redux","description":"Use this when moving a legacy Redux codebase to current RTK patterns. Covers replacing createStore with configureStore, migrating touched reducers to createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks with RTK Query instead of writing new legacy Redux code.\n","type":"lifecycle","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["build-modern-redux-apps/modern-redux"],"sources":["reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx","reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md","reduxjs/redux-toolkit:packages/rtk-codemods/README.md","reduxjs/redux:docs/style-guide/style-guide.md"]},"isInternal":false,"tokens":1448,"sizeBytes":5837},{"name":"endpoint-lifecycle.md","path":"packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Endpoint Lifecycle\n\n## Invalidation rule\n\nWhen a mutation invalidates tags:\n\n- active subscribers refetch\n- inactive cache entries are removed\n- removed entries fetch again only when something subscribes later\n\nThat behavior is deliberate; invalidation is not a background \"refresh everything\" switch.\n\n## Document cache tradeoff\n\nRTK Query is a document cache, not a normalized entity graph cache.\n\nUse RTK Query by default when:\n\n- the data comes from request/response APIs\n- document caching is acceptable\n- tag invalidation and endpoint lifecycles solve the problem\n\nReach for a different tool when:\n\n- the real requirement is a normalized graph cache\n- the stack already has a domain-specific normalized client that fits better\n\nIf normalized caching is mandatory and no better library is already in the stack, a slice plus thunk flow may be the fallback.\n\n## Useful endpoint options\n\n- `providesTags`: tell RTK Query what cache entries this query represents\n- `invalidatesTags`: tell RTK Query what a mutation dirties\n- `onQueryStarted`: optimistic and pessimistic updates tied to a request\n- `onCacheEntryAdded`: long-lived subscriptions such as streaming data\n- `keepUnusedDataFor`: how long inactive cache entries stay around\n","isInternal":false,"tokens":255,"sizeBytes":1237},{"name":"SKILL.md","path":"packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md","title":"manage-server-data/adopt-rtk-query","category":"anthropic-skill","format":"markdown","content":"---\nname: manage-server-data/adopt-rtk-query\ndescription: >\n  Use this when adding RTK Query as the default server-data and document-cache\n  layer. Covers createApi, store integration, hooks, invalidation behavior,\n  optimistic updates, and deciding when RTK Query is the right cache model.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Adopt RTK Query\n\n## Setup\n\n```tsx\n// file: src/services/api.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    addPost: build.mutation<Post, Pick<Post, 'title'>>({\n      query: (body) => ({\n        url: 'posts',\n        method: 'POST',\n        body,\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n\nexport const { useGetPostsQuery, useAddPostMutation } = api\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { api } from '../services/api'\n\nexport const store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n\n// file: src/App.tsx\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { useAddPostMutation, useGetPostsQuery } from './services/api'\n\nfunction Posts() {\n  const { data: posts = [] } = useGetPostsQuery()\n  const [addPost] = useAddPostMutation()\n\n  return (\n    <div>\n      <button onClick={() => addPost({ title: 'Write docs' })}>Add</button>\n      <ul>\n        {posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nexport function App() {\n  return (\n    <Provider store={store}>\n      <Posts />\n    </Provider>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep one API slice per base URL and extend it\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n\nexport const postsApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getPosts: build.query<{ id: string; title: string }[], void>({\n      query: () => 'posts',\n    }),\n  }),\n})\n```\n\nSplit files with `injectEndpoints`, not by making multiple `createApi` roots for the same backend.\n\n### Use tags for cache invalidation\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: (_result, _error, { id }) => [{ type: 'Post', id }],\n    }),\n  }),\n})\n```\n\nTreat tags as the normal invalidation path before reaching for manual cache patching.\n\n### Do optimistic updates in endpoint lifecycles\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n        const patch = dispatch(\n          api.util.updateQueryData('getPosts', undefined, (draft) => {\n            const post = draft.find((item) => item.id === id)\n            if (post) {\n              post.title = title\n            }\n          }),\n        )\n\n        try {\n          await queryFulfilled\n        } catch {\n          patch.undo()\n        }\n      },\n    }),\n  }),\n})\n```\n\nKeep optimistic and pessimistic cache updates inside endpoint lifecycle handlers so they stay coupled to the request.\n\n## Common Mistakes\n\n### CRITICAL Creating multiple API slices for one backend\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst postsApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getUsers: build.query<User[], void>({ query: () => 'users' }),\n  }),\n})\n```\n\nOne API slice per base URL preserves invalidation behavior and avoids duplicated middleware work.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx\n\n### HIGH Forgetting `api.reducer` or `api.middleware`\n\nWrong:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n```\n\nRTK Query hooks need both the reducer and middleware to manage cache state and request lifecycles.\n\nSource: reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx\n\n### MEDIUM Persisting browser API cache by default\n\nWrong:\n\n```ts\nconst storage = window.localStorage\n\nconst persistConfig = {\n  key: 'root',\n  storage,\n}\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n```\n\nPersisting RTK Query cache in browsers often keeps stale data around longer than users expect; treat persistence as a special case, not the default.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx\n\n### HIGH Patching cache from components\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst dispatch = useAppDispatch()\n\nuseEffect(() => {\n  dispatch(\n    api.util.updateQueryData('getPosts', undefined, (draft) => {\n      draft.push({ id: 'p3', title: 'Patched from component' })\n    }),\n  )\n}, [dispatch])\n```\n\nCorrect:\n\n```ts\nupdatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n  query: ({ id, title }) => ({\n    url: `posts/${id}`,\n    method: 'PATCH',\n    body: { title },\n  }),\n  async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n    const patch = dispatch(\n      api.util.updateQueryData('getPosts', undefined, (draft) => {\n        const post = draft.find((item) => item.id === id)\n        if (post) {\n          post.title = title\n        }\n      }),\n    )\n\n    try {\n      await queryFulfilled\n    } catch {\n      patch.undo()\n    }\n  },\n})\n```\n\nComponent-level cache patches drift away from the mutation lifecycle that should own them.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx\n\n### HIGH Expecting invalidation to refetch unsubscribed queries\n\nWrong:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nconst subscription = store.dispatch(api.endpoints.getPosts.initiate())\nsubscription.unsubscribe()\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nCorrect:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nstore.dispatch(api.endpoints.getPosts.initiate())\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nInvalidation only refetches actively subscribed queries; if no component is using that cache entry, RTK Query drops it and fetches again next time it is needed.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx\n\n## References\n\n- [Endpoint lifecycle details and cache tradeoffs](references/endpoint-lifecycle.md)\n","frontmatter":{"name":"manage-server-data/adopt-rtk-query","description":"Use this when adding RTK Query as the default server-data and document-cache layer. Covers createApi, store integration, hooks, invalidation behavior, optimistic updates, and deciding when RTK Query is the right cache model.\n","type":"lifecycle","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["build-modern-redux-apps/modern-redux"],"sources":["reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx","reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx","reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx","reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx","reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx","reduxjs/redux:docs/style-guide/style-guide.md"]},"isInternal":false,"tokens":2364,"sizeBytes":9281},{"name":"slice-patterns.md","path":"packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Slice Patterns\n\n## `getSelectors` for alternate mounting points\n\n```ts\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {},\n  selectors: {\n    selectValue: (state) => state.value,\n  },\n})\n\nexport const { selectValue } = counterSlice.getSelectors(\n  (state: { customCounter: { value: number } }) => state.customCounter,\n)\n```\n\nUse `getSelectors` when the slice is not mounted at its default `reducerPath`.\n\n## `withLazyLoadedSlices` and `injectInto`\n\n```ts\nimport { combineSlices, createSlice, type WithSlice } from '@reduxjs/toolkit'\n\nconst staticSlice = createSlice({\n  name: 'static',\n  initialState: { ready: true },\n  reducers: {},\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices(staticSlice).withLazyLoadedSlices<LazyLoadedSlices>()\n\nconst lazySlice = createSlice({\n  name: 'lazy',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\ndeclare module '../rootReducer' {\n  export interface LazyLoadedSlices extends WithSlice<typeof lazySlice> {}\n}\n\nexport const injectedLazySlice = lazySlice.injectInto(rootReducer)\n```\n\nThis keeps RootState types aware of reducers that will be injected later.\n\n## Selector caveat\n\nIf a selector depends on caller-specific arguments and must be memoized per caller, prefer a selector factory outside `createSlice.selectors`. `createSlice.selectors` gives you one selector instance, not a selector factory.\n","isInternal":false,"tokens":347,"sizeBytes":1482},{"name":"SKILL.md","path":"packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md","title":"model-redux-state/build-slices-and-selectors","category":"anthropic-skill","format":"markdown","content":"---\nname: model-redux-state/build-slices-and-selectors\ndescription: >\n  Use this when authoring or refactoring slices with createSlice, selectors,\n  create.asyncThunk, entity adapters, or lazy reducer injection. Covers\n  Immer-backed mutation syntax, slice selectors, getSelectors, injectInto,\n  withLazyLoadedSlices, and current RTK 2 slice patterns.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - model-redux-state/design-state-ownership\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createSlice.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/combineSlices.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/immer-reducers.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Build Slices And Selectors\n\n## Setup\n\n```ts\n// file: src/app/createAppSlice.ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nexport const createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\n// file: src/features/posts/postsSlice.ts\nimport { createSelector } from '@reduxjs/toolkit'\nimport { createAppSlice } from '../../app/createAppSlice'\n\ntype PostsState = {\n  items: { id: string; title: string; published: boolean }[]\n  status: 'idle' | 'pending' | 'succeeded' | 'failed'\n}\n\nconst initialState: PostsState = {\n  items: [],\n  status: 'idle',\n}\n\nexport const postsSlice = createAppSlice({\n  name: 'posts',\n  initialState,\n  reducers: (create) => ({\n    postAdded: create.reducer<{ id: string; title: string }>(\n      (state, action) => {\n        state.items.push({ ...action.payload, published: false })\n      },\n    ),\n    fetchPosts: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/posts')\n        return (await response.json()) as {\n          id: string\n          title: string\n          published: boolean\n        }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'succeeded'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n  selectors: {\n    selectPosts: (state) => state.items,\n    selectPublishedPosts: createSelector(\n      [(state: PostsState) => state.items],\n      (items) => items.filter((post) => post.published),\n    ),\n  },\n})\n\nexport const { postAdded, fetchPosts } = postsSlice.actions\nexport const { selectPosts, selectPublishedPosts } = postsSlice.selectors\n```\n\n## Core Patterns\n\n### Use mutating logic inside slice reducers\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string; text: string; done: boolean }[],\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.push({ ...action.payload, done: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.done = !todo.done\n      }\n    },\n  },\n})\n```\n\nImmer is the default inside `createSlice`; write the reducer logic directly instead of copying arrays and objects by hand.\n\n### Define selectors in the slice when they belong to the slice\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n  selectors: {\n    selectValue: (state) => state.value,\n    selectIsPositive: (state) => state.value > 0,\n  },\n})\n\nconst { selectValue, selectIsPositive } = counterSlice.selectors\n```\n\nSlice selectors keep state-location knowledge next to the slice.\n\n### Use `create.asyncThunk` when the async lifecycle belongs to the slice\n\n```ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nconst createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\nconst usersSlice = createAppSlice({\n  name: 'users',\n  initialState: {\n    items: [] as { id: string; name: string }[],\n    status: 'idle' as 'idle' | 'pending' | 'failed',\n  },\n  reducers: (create) => ({\n    fetchUsers: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/users')\n        return (await response.json()) as { id: string; name: string }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'idle'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n})\n```\n\nUse this when the async lifecycle handlers naturally live with the slice; otherwise regular `createAsyncThunk` is still fine.\n\n### Use entity adapters and lazy injection for scalable slices\n\n```ts\nimport {\n  combineSlices,\n  createEntityAdapter,\n  createSlice,\n} from '@reduxjs/toolkit'\n\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n\nconst booksSlice = createSlice({\n  name: 'books',\n  initialState: booksAdapter.getInitialState(),\n  reducers: {\n    booksReceived: booksAdapter.setAll,\n  },\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices().withLazyLoadedSlices<LazyLoadedSlices>()\n\ndeclare module './rootReducer' {\n  export interface LazyLoadedSlices {}\n}\n\nconst injectedBooksSlice = booksSlice.injectInto(rootReducer)\n\nconst selectors = booksAdapter.getSelectors(\n  (state: ReturnType<typeof rootReducer.selector.original>) =>\n    injectedBooksSlice.selectSlice(state),\n)\n```\n\nEntity adapters standardize normalized collections, and `injectInto` lets a slice stay aware of its injected location.\n\n## Common Mistakes\n\n### CRITICAL Using mutating logic outside slice reducers\n\nWrong:\n\n```ts\ntype Todo = { id: string; text: string }\n\nexport function addTodo(todos: Todo[], todo: Todo) {\n  todos.push(todo)\n  return todos\n}\n```\n\nCorrect:\n\n```ts\ntype Todo = { id: string; text: string }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as Todo[],\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nMutation syntax is only safe inside Immer-backed reducer contexts such as `createSlice` and `createReducer`.\n\nSource: reduxjs/redux-toolkit:docs/usage/immer-reducers.md\n\n### HIGH Writing hand-written switch reducers as the default\n\nWrong:\n\n```ts\nexport default function todosReducer(\n  state = initialState,\n  action: { type: string; payload?: Todo },\n) {\n  switch (action.type) {\n    case 'todos/todoAdded':\n      return state.concat(action.payload as Todo)\n    default:\n      return state\n  }\n}\n```\n\nCorrect:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nHand-written reducers are an escape hatch for proven bottlenecks, not the normal thing an agent should generate in RTK code.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Writing RTK 1.x object syntax for `extraReducers`\n\nWrong:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: {\n    [fetchPosts.fulfilled.type]: (state, action) => {\n      state.items = action.payload\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: (builder) => {\n    builder.addCase(fetchPosts.fulfilled, (state, action) => {\n      state.items = action.payload\n    })\n  },\n})\n```\n\nRTK 2 removed the object form; agents trained on RTK 1.x still generate it.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Assuming `entity.id` exists for every collection\n\nWrong:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>()\n```\n\nCorrect:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n```\n\nAdapters default to `entity.id`; collections keyed by another field must provide `selectId`.\n\nSource: reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx\n\n## References\n\n- [Slice selectors, async creators, and lazy injection details](references/slice-patterns.md)\n","frontmatter":{"name":"model-redux-state/build-slices-and-selectors","description":"Use this when authoring or refactoring slices with createSlice, selectors, create.asyncThunk, entity adapters, or lazy reducer injection. Covers Immer-backed mutation syntax, slice selectors, getSelectors, injectInto, withLazyLoadedSlices, and current RTK 2 slice patterns.\n","type":"core","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["model-redux-state/design-state-ownership"],"sources":["reduxjs/redux-toolkit:docs/api/createSlice.mdx","reduxjs/redux-toolkit:docs/api/combineSlices.mdx","reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx","reduxjs/redux-toolkit:docs/usage/immer-reducers.md","reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md","reduxjs/redux:docs/style-guide/style-guide.md"]},"isInternal":false,"tokens":2263,"sizeBytes":9222},{"name":"state-ownership.md","path":"packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# State Ownership Heuristics\n\n## Choose the owner, then the tool\n\n| Kind of state                              | Default owner   | Typical tool                             |\n| ------------------------------------------ | --------------- | ---------------------------------------- |\n| Editable form fields                       | Component       | `useState`                               |\n| Shared mutable app data                    | Redux           | Slice state                              |\n| Server cache                               | RTK Query       | `createApi`                              |\n| URL, pathname, search params               | Router          | Router APIs plus selector inputs         |\n| Browser-only authority like `localStorage` | External source | Read at boundaries, then dispatch events |\n\n## Good reasons to move data into Redux\n\n- Multiple distant parts of the UI need the same mutable data.\n- You need time-travel debugging or a stable action history.\n- The reducer should own transitions because they mix old store state with new inputs.\n\n## Reasons to keep data out of Redux\n\n- Another system already owns it, such as the router.\n- It only matters during editing inside one component tree.\n- It is server cache and RTK Query fits the use case better.\n\n## Re-evaluate slice size\n\n- If data is constantly stitched together outside reducers, it may belong closer together.\n- If unrelated updates keep touching the same slice, split the slice by domain ownership.\n","isInternal":false,"tokens":262,"sizeBytes":1498},{"name":"SKILL.md","path":"packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md","title":"model-redux-state/design-state-ownership","category":"anthropic-skill","format":"markdown","content":"---\nname: model-redux-state/design-state-ownership\ndescription: >\n  Use this when deciding whether data belongs in Redux, component state, router\n  state, or another external source. Covers state ownership, authority\n  boundaries, slice sizing, and when to move or split data as the app evolves.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md'\n---\n\n# Design State Ownership\n\n## Setup\n\n```tsx\nimport { useState } from 'react'\nimport { createSlice } from '@reduxjs/toolkit'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string; content: string }[],\n  reducers: {\n    postAdded(\n      state,\n      action: { payload: { id: string; title: string; content: string } },\n    ) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst { postAdded } = postsSlice.actions\n\nexport function AddPostForm() {\n  const [title, setTitle] = useState('')\n  const [content, setContent] = useState('')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(postAdded({ id: 'p1', title, content }))\n      }}\n    >\n      <input value={title} onChange={(event) => setTitle(event.target.value)} />\n      <textarea\n        value={content}\n        onChange={(event) => setContent(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep editable form state local until the user commits it\n\n```tsx\nimport { useState } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\nimport { profileSaved } from './profileSlice'\n\nexport function ProfileForm() {\n  const [displayName, setDisplayName] = useState('Lenz')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(profileSaved({ displayName }))\n      }}\n    >\n      <input\n        value={displayName}\n        onChange={(event) => setDisplayName(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\nPrefer Redux for shared, durable app state, not every keystroke.\n\n### Keep URL state with the router and combine it at the edge\n\n```tsx\nimport { createSelector } from '@reduxjs/toolkit'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppSelector } from '../../app/hooks'\n\ntype RootState = {\n  posts: {\n    items: { id: string; title: string; published: boolean }[]\n  }\n}\n\nconst selectPosts = (state: RootState) => state.posts.items\n\nconst selectVisiblePosts = createSelector(\n  [selectPosts, (_state: RootState, filter: string) => filter],\n  (posts, filter) =>\n    filter === 'published' ? posts.filter((post) => post.published) : posts,\n)\n\nexport function PostsList() {\n  const [searchParams] = useSearchParams()\n  const filter = searchParams.get('filter') ?? 'all'\n  const posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n\n  return <div>{posts.length}</div>\n}\n```\n\nIf the router already owns a piece of state, pass it into selectors or combine it in the component instead of syncing it into Redux.\n\n### Re-size slices when access patterns change\n\n```ts\nimport { combineReducers, createSlice } from '@reduxjs/toolkit'\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: { items: [] as { id: string; title: string }[] },\n  reducers: {},\n})\n\nexport const rootReducer = combineReducers({\n  auth: authSlice.reducer,\n  posts: postsSlice.reducer,\n})\n```\n\nRevisit slice size over time; unrelated data should split apart, and data constantly stitched together in every component may belong closer together.\n\n## Common Mistakes\n\n### MEDIUM Putting form editing state in Redux\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nconst selectDraftTitle = (state: { draft: { title: string } }) =>\n  state.draft.title\n\nconst title = useAppSelector(selectDraftTitle)\n```\n\nCorrect:\n\n```tsx\nconst [title, setTitle] = useState('')\n\n<input value={title} onChange={(event) => setTitle(event.target.value)} />\n```\n\nPer-keystroke dispatching adds global complexity for data that usually lives in one component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Synchronizing router or URL state into Redux\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppDispatch } from '../../app/hooks'\n\nfunction PostsPage() {\n  const [searchParams] = useSearchParams()\n  const dispatch = useAppDispatch()\n\n  useEffect(() => {\n    dispatch(filterChanged(searchParams.get('filter') ?? 'all'))\n  }, [dispatch, searchParams])\n\n  return null\n}\n```\n\nCorrect:\n\n```tsx\nconst filter = searchParams.get('filter') ?? 'all'\nconst posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n```\n\nURL state already has an authoritative owner; duplicating it into Redux creates two sources of truth.\n\nSource: maintainer interview\n\n### HIGH Naming state after components\n\nWrong:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst loginReducer = (state = { open: false }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  loginScreen: loginReducer,\n  postsList: postsReducer,\n})\n```\n\nCorrect:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst authReducer = (state = { userId: null as string | null }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  auth: authReducer,\n  posts: postsReducer,\n})\n```\n\nStore keys should describe data or domain concepts, not the current component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Letting slice boundaries fossilize\n\nWrong:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\ntype AppNotification = { id: string; message: string }\n\nconst appSlice = createSlice({\n  name: 'app',\n  initialState: {\n    auth: { userId: null as string | null },\n    posts: [] as Post[],\n    notifications: [] as AppNotification[],\n  },\n  reducers: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as Post[],\n  reducers: {},\n})\n```\n\nWhen unrelated data is welded together, every change point gets noisier; split or merge slices as actual access patterns demand.\n\nSource: maintainer interview\n\n### HIGH Blindly spreading payloads into state\n\nWrong:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  return { ...state, ...action.payload }\n}\n```\n\nCorrect:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  state.id = action.payload.id\n  state.name = action.payload.name\n}\n```\n\nReducers should own the slice shape instead of treating payloads as trusted state patches.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n## References\n\n- [State ownership heuristics](references/state-ownership.md)\n","frontmatter":{"name":"model-redux-state/design-state-ownership","description":"Use this when deciding whether data belongs in Redux, component state, router state, or another external source. Covers state ownership, authority boundaries, slice sizing, and when to move or split data as the app evolves.\n","type":"core","library":"@reduxjs/toolkit","library_version":"2.11.2","sources":["reduxjs/redux:docs/style-guide/style-guide.md","reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md","reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md"]},"isInternal":false,"tokens":1839,"sizeBytes":7668},{"name":"listener-workflows.md","path":"packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# Listener Workflows\n\n## Decision table\n\n| Need                                                       | Reach for                     |\n| ---------------------------------------------------------- | ----------------------------- |\n| Cached server data                                         | RTK Query                     |\n| One imperative async workflow with `dispatch` / `getState` | `createAsyncThunk` or a thunk |\n| React to later actions or state transitions                | `createListenerMiddleware`    |\n\nA good app often mixes imperative and reactive workflows. The split is by job, not by ideology.\n\n## Useful listener helpers\n\n- `predicate`: react to any action when a state condition becomes true\n- `condition`: wait until a condition becomes true before continuing\n- `take`: wait for the next matching action\n- `cancelActiveListeners`: cancel older instances of the same workflow\n- `fork`: start a child task\n\n## Example: cancel stale work\n\n```ts\nstartAppListening({\n  actionCreator: searchRequested,\n  effect: async (action, listenerApi) => {\n    listenerApi.cancelActiveListeners()\n    await listenerApi.delay(250)\n    listenerApi.dispatch(searchStarted(action.payload))\n  },\n})\n```\n\nThis is the kind of long-lived reactive behavior that does not fit a thunk well.\n","isInternal":false,"tokens":241,"sizeBytes":1284},{"name":"SKILL.md","path":"packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md","title":"orchestrate-side-effects/handle-side-effects","category":"anthropic-skill","format":"markdown","content":"---\nname: orchestrate-side-effects/handle-side-effects\ndescription: >\n  Use this when choosing between RTK Query, createAsyncThunk, handwritten\n  thunks, and createListenerMiddleware. Covers imperative versus reactive\n  workflows, listener middleware setup, and keeping side effects out of\n  reducers and UI components.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n---\n\n# Handle Side Effects\n\n## Setup\n\n```ts\nimport {\n  configureStore,\n  createListenerMiddleware,\n  createSlice,\n} from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveStarted(state) {\n      state.status = 'idle'\n    },\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nexport const store = configureStore({\n  reducer: {\n    docs: docsSlice.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\nexport const startAppListening = listenerMiddleware.startListening.withTypes<\n  RootState,\n  AppDispatch\n>()\n```\n\n## Core Patterns\n\n### Use RTK Query for server cache by default\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf the problem is server data that should be cached and re-used, start with RTK Query instead of a thunk.\n\n### Use `createAsyncThunk` for imperative workflows\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Draft = { title: string }\n\nexport const draftSaved = createAsyncThunk(\n  'drafts/save',\n  async (draft: Draft) => {\n    const response = await fetch('/api/drafts', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(draft),\n    })\n\n    return (await response.json()) as { id: string; title: string }\n  },\n)\n\nconst draftsSlice = createSlice({\n  name: 'drafts',\n  initialState: { status: 'idle' as 'idle' | 'pending' | 'failed' },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(draftSaved.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(draftSaved.fulfilled, (state) => {\n        state.status = 'idle'\n      })\n      .addCase(draftSaved.rejected, (state) => {\n        state.status = 'failed'\n      })\n  },\n})\n```\n\nUse a thunk when you need one imperative async workflow with `dispatch` and `getState`.\n\n### Use listener middleware for reactive workflows\n\n```ts\nimport { createListenerMiddleware, createSlice } from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst notificationsSlice = createSlice({\n  name: 'notifications',\n  initialState: [] as string[],\n  reducers: {\n    notificationQueued(state, action: { payload: string }) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nlistenerMiddleware.startListening({\n  actionCreator: docsSlice.actions.saveFinished,\n  effect: async (_action, listenerApi) => {\n    listenerApi.dispatch(\n      notificationsSlice.actions.notificationQueued('Document saved'),\n    )\n  },\n})\n```\n\nListeners fit workflows that react to future actions or state changes over time instead of driving one imperative request from a single callsite.\n\n## Common Mistakes\n\n### CRITICAL Running side effects inside reducers\n\nWrong:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string }[],\n  reducers: {\n    todoSaved(state, action: { payload: { id: string } }) {\n      fetch('/api/todos', { method: 'POST' })\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\nconst todoSaved = createAsyncThunk(\n  'todos/save',\n  async (todo: { id: string }) => {\n    await fetch('/api/todos', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(todo),\n    })\n    return todo\n  },\n)\n```\n\nReducers must stay pure even when Immer is available.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using thunks to watch future state changes\n\nWrong:\n\n```ts\nexport const waitForSave =\n  () =>\n  async (_dispatch: unknown, getState: () => { docs: { status: string } }) => {\n    while (getState().docs.status !== 'saved') {\n      await new Promise((resolve) => setTimeout(resolve, 100))\n    }\n  }\n```\n\nCorrect:\n\n```ts\nstartAppListening({\n  predicate: (_action, currentState) => currentState.docs.status === 'saved',\n  effect: async () => {\n    console.log('Document saved')\n  },\n})\n```\n\nPolling inside thunks fights the architecture; listener middleware is the reactive tool.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n### HIGH Appending listener middleware after the default checks\n\nWrong:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(listenerMiddleware.middleware),\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n```\n\nListener add and remove actions may carry functions, so the listener middleware needs to run before serializability checks.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n## References\n\n- [Listener helpers and decision heuristics](references/listener-workflows.md)\n","frontmatter":{"name":"orchestrate-side-effects/handle-side-effects","description":"Use this when choosing between RTK Query, createAsyncThunk, handwritten thunks, and createListenerMiddleware. Covers imperative versus reactive workflows, listener middleware setup, and keeping side effects out of reducers and UI components.\n","type":"core","library":"@reduxjs/toolkit","library_version":"2.11.2","requires":["build-modern-redux-apps/redux-dataflow"],"sources":["reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx","reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx","reduxjs/redux:docs/style-guide/style-guide.md","reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md"]},"isInternal":false,"tokens":1571,"sizeBytes":6664},{"name":"README.md","path":"packages/rtk-codemods/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-codemods/README.md","title":"rtk-codemods Documentation","category":"plugin-manifest","format":"markdown","content":"# RTK Codemods\n\nA collection of codemods for updating legacy Redux Toolkit API usage patterns to modern patterns.\n\n## Usage\n\nTo run a specific codemod from this project, you would run the following:\n\n```bash\nnpx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Transforms\n\n<!--TRANSFORMS_START-->\n\n- [createReducerBuilder](transforms/createReducerBuilder/README.md)\n- [createSliceBuilder](transforms/createSliceBuilder/README.md)\n- [createSliceReducerBuilder](transforms/createSliceReducerBuilder/README.md)\n\n<!--TRANSFORMS_END-->\n\n## Contributing\n\n### Installation\n\n- clone the repo\n- change into the repo directory\n- `pnpm install`\n\n### Running tests\n\n- `pnpm --filter @reduxjs/rtk-codemods test`\n","isInternal":false,"tokens":254,"sizeBytes":935},{"name":"README.md","path":"packages/rtk-codemods/transforms/createReducerBuilder/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-codemods/transforms/createReducerBuilder/README.md","title":"createReducerBuilder Documentation","category":"plugin-manifest","format":"markdown","content":"# createReducerBuilder\n\nRewrites uses of the \"object\" syntax for Redux Toolkit's `createReducer` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\ncreateReducer(initialState, {\n  [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n    // stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded](state: SliceState, action: PayloadAction<string>) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createReducerBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\ncreateReducer(initialState, {\n  [todoAdded1a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded1b]: (state, action) => action.payload,\n  [todoAdded1c + 'test']: (state, action) => {\n    // stuff\n  },\n  [todoAdded1d](state, action) {\n    // stuff\n  },\n  [todoAdded1e]: function (state, action) {\n    // stuff\n  },\n  todoAdded1f: (state, action) => {\n    //stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded2a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded2b](state, action) {\n    // stuff\n  },\n  [todoAdded2c]: function (state, action) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createReducerBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded1a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n  builder.addCase(todoAdded1c + 'test', (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1d, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1e, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1f, (state, action) => {\n    //stuff\n  })\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded2a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2b, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2c, (state, action) => {\n    // stuff\n  })\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","isInternal":false,"tokens":922,"sizeBytes":3187},{"name":"README.md","path":"packages/rtk-codemods/transforms/createSliceBuilder/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-codemods/transforms/createSliceBuilder/README.md","title":"createSliceBuilder Documentation","category":"plugin-manifest","format":"markdown","content":"# createSliceBuilder\n\nRewrites uses of the `extraReducers` \"object\" syntax for Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n  extraReducers: {\n    [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n  extraReducers: {\n    [todoAdded](state: SliceState, action: PayloadAction<string>) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createSliceBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n  extraReducers: {\n    [todoAdded1a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded1b]: (state, action) => action.payload,\n    [todoAdded1c + 'test']: (state, action) => {\n      // stuff\n    },\n    [todoAdded1d](state, action) {\n      // stuff\n    },\n    [todoAdded1e]: function (state, action) {\n      // stuff\n    },\n    todoAdded1f: (state, action) => {\n      //stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n  extraReducers: {\n    [todoAdded2a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded2b](state, action) {\n      // stuff\n    },\n    [todoAdded2c]: function (state, action) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createSliceBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded1a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n    builder.addCase(todoAdded1c + 'test', (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1d, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1e, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1f, (state, action) => {\n      //stuff\n    })\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded2a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2b, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2c, (state, action) => {\n      // stuff\n    })\n  }\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n","isInternal":false,"tokens":1068,"sizeBytes":3749},{"name":"README.md","path":"packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md","title":"createSliceReducerBuilder Documentation","category":"plugin-manifest","format":"markdown","content":"# createSliceReducerBuilder\n\nRewrites uses of Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax for the `reducers` field, to make it easier to add prepared reducers and thunks inside of `createSlice`.\n\nNote that unlike the `createReducerBuilder` and `createSliceBuilder` transforms (which both were fixes for deprecated/removed overloads), this is entirely optional. You do not _need_ to apply this to an entire codebase unless you specifically want to. Otherwise, feel free to apply to specific slice files as needed.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```\nnpx @reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\n@reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n<!--FIXTURES_TOC_END-->\n\n<!--FIXTURES_CONTENT_START-->\n<!--FIXTURES_CONTENT_END-->\n","isInternal":false,"tokens":266,"sizeBytes":1045},{"name":"README.md","path":"packages/rtk-query-codegen-openapi/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/rtk-query-codegen-openapi/README.md","title":"rtk-query-codegen-openapi Documentation","category":"plugin-manifest","format":"markdown","content":"<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/rtk-incubator/rtk-query/main/logo.png\" width=\"400\" />\n</p>\n<h2 align=\"center\">\nCode Generator\n</h2>\n\n<p align=\"center\">\n   <a href=\"https://discord.gg/0ZcbPKXt5bZ6au5t\" target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/chat-online-green\" alt=\"Discord server\" />\n  </a>\n</p>\n\n### Introduction\n\nThis is a utility library meant to be used with [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) that will generate a typed API client from an OpenAPI schema.\n\n### Documentation\n\n[View the RTK Query Code Generation docs](https://redux-toolkit.js.org/rtk-query/usage/code-generation)\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n","isInternal":false,"tokens":213,"sizeBytes":755},{"name":"README.md","path":"packages/toolkit/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/README.md","title":"toolkit Documentation","category":"plugin-manifest","format":"markdown","content":"# Redux Toolkit\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-toolkit/tests.yml?style=flat-square)\n[![npm version](https://img.shields.io/npm/v/@reduxjs/toolkit.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/toolkit)\n[![npm downloads](https://img.shields.io/npm/dm/@reduxjs/toolkit.svg?style=flat-square&label=RTK+downloads)](https://www.npmjs.com/package/@reduxjs/toolkit)\n\n**The official, opinionated, batteries-included toolset for efficient Redux development**\n\n## Installation\n\n### Create a React Redux App\n\nThe recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).\n\nBoth of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.\n\n```bash\n# Vite with our Redux+TS template\n# (using the `tiged` tool to clone and extract the template)\nnpx tiged reduxjs/redux-templates/packages/vite-template-redux my-app\n\n# Next.js using the `with-redux` template\nnpx create-next-app --example with-redux my-app\n```\n\nFor React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):\n\n```bash\nnpx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app\n```\n\n### An Existing App\n\nRedux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:\n\n```bash\n# NPM\nnpm install @reduxjs/toolkit\n\n# Yarn\nyarn add @reduxjs/toolkit\n```\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n\nThe package includes a precompiled browser ESM build that can be loaded from a `<script type=\"module\">` tag. No-bundler browser usage also needs an import map, because the build imports Redux Toolkit's dependencies by package name. See the [Getting Started docs](https://redux-toolkit.js.org/introduction/getting-started#an-existing-app) for an example.\n\n## Documentation\n\nThe Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit.\n\nThe Redux core docs at https://redux.js.org includes the full Redux tutorials, as well usage guides on general Redux patterns.\n\n## Purpose\n\nThe **Redux Toolkit** package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:\n\n- \"Configuring a Redux store is too complicated\"\n- \"I have to add a lot of packages to get Redux to do anything useful\"\n- \"Redux requires too much boilerplate code\"\n\nWe can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.\n\nBecause of that, this package is deliberately limited in scope. It does _not_ address concepts like \"reusable encapsulated Redux modules\", folder or file structures, managing entity relationships in the store, and so on.\n\nRedux Toolkit also includes a powerful data fetching and caching capability that we've dubbed \"RTK Query\". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.\n\n## What's Included\n\nRedux Toolkit includes these APIs:\n\n- `configureStore()`: wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, add whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.\n- `createReducer()`: lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/mweststrate/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.\n- `createAction()`: generates an action creator function for the given action type string. The function itself has `toString()` defined, so that it can be used in place of the type constant.\n- `createSlice()`: combines `createReducer()` + `createAction()`. Accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.\n- `combineSlices()`: combines multiple slices into a single reducer, and allows \"lazy loading\" of slices after initialisation.\n- `createListenerMiddleware()`: lets you define \"listener\" entries that contain an \"effect\" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes. A lightweight alternative to Redux async middleware like sagas and observables.\n- `createAsyncThunk()`: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/resolved/rejected` action types based on that promise\n- `createEntityAdapter()`: generates a set of reusable reducers and selectors to manage normalized data in the store\n- The `createSelector()` utility from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.\n\nFor details, see [the Redux Toolkit API Reference section in the docs](https://redux-toolkit.js.org/api/configureStore).\n\n## RTK Query\n\n**RTK Query** is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.\n\nRTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](https://redux.js.org/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.\n\nRTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:\n\n```ts no-transpile\nimport { createApi } from '@reduxjs/toolkit/query'\n\n/* React-specific entry point that automatically generates\n   hooks corresponding to the defined endpoints */\nimport { createApi } from '@reduxjs/toolkit/query/react'\n```\n\n### What's included\n\nRTK Query includes these APIs:\n\n- `createApi()`: The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with \"one API slice per base URL\" as a rule of thumb.\n- `fetchBaseQuery()`: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.\n- `<ApiProvider />`: Can be used as a Provider if you do not already have a Redux store.\n- `setupListeners()`: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.\n\nSee the [**RTK Query Overview**](https://redux-toolkit.js.org/rtk-query/overview) page for more details on what RTK Query is, what problems it solves, and how to use it.\n\n## Contributing\n\nPlease refer to our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Redux Toolkit.\n","isInternal":false,"tokens":1821,"sizeBytes":8286},{"name":"README.md","path":"packages/toolkit/scripts/issue-triage/README.md","rawUrl":"https://raw.githubusercontent.com/reduxjs/redux-toolkit/HEAD/packages/toolkit/scripts/issue-triage/README.md","title":"issue-triage Documentation","category":"plugin-manifest","format":"markdown","content":"# GitHub Issues Triage Tool\n\nA TypeScript-based tool for automatically triaging and categorizing GitHub issues in the Redux Toolkit repository.\n\n## Features\n\n- Fetches open issues from GitHub API\n- Categorizes issues based on labels, titles, and content\n- Generates markdown reports with statistics\n- Caches data to avoid API rate limiting\n- Supports both Node.js and Bun runtimes\n\n## Setup\n\n1. Install dependencies:\n\n```bash\nnpm install\n```\n\n2. Build the TypeScript code:\n\n```bash\nnpm run build\n```\n\n## Usage\n\nRun the triage tool:\n\n```bash\nnpm start\n```\n\nOr for development (builds and runs):\n\n```bash\nnpm run dev\n```\n\n### Using Cached Data\n\nThe tool automatically saves fetched data to `cache/issues-data.json` after each run. To use cached data instead of fetching fresh data (useful for development and testing):\n\n```bash\nnpm start -- --use-cache\n```\n\nOr with the dev command:\n\n```bash\nnpm run dev -- --use-cache\n```\n\n**Note:** The `--use-cache` flag is primarily for development/testing purposes. It loads data from the cache file instantly without making API calls. If the cache file doesn't exist or is corrupted, the tool will automatically fetch fresh data.\n\n## Project Structure\n\n```\nscripts/issue-triage/\n├── src/           # TypeScript source files\n├── dist/          # Compiled JavaScript output\n├── cache/         # Cached JSON data from GitHub\n├── reports/       # Generated triage reports\n├── package.json   # Project dependencies\n└── tsconfig.json  # TypeScript configuration\n```\n\n## Requirements\n\n- Node.js 18+ or Bun 1.0+\n- TypeScript 5.0+\n\n## Development\n\nThis tool is built with TypeScript using ES modules (ESM). The configuration supports both Node.js and Bun runtimes for maximum flexibility.\n","isInternal":false,"tokens":400,"sizeBytes":1753}],"systemPromptSnippet":"<yakaai_skills repo=\"reduxjs/redux-toolkit\">\n<!-- File: packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/references/codegen-overrides.md (Tokens: ~274 | Category: anthropic-skill) -->\n# Codegen Overrides\n\n## Common override targets\n\n| Need                                            | Override                           |\n| ----------------------------------------------- | ---------------------------------- |\n| Query should really be a mutation               | `type: 'mutation'`                 |\n| Header or optional parameters should be omitted | `parameterFilter`                  |\n| Generated tags are too coarse                   | `providesTags` / `invalidatesTags` |\n\n## Example: split one schema into multiple outputs\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFiles: {\n    './src/store/userApi.ts': {\n      exportName: 'userApi',\n      filterEndpoints: [/user/i],\n      hooks: true,\n    },\n    './src/store/orderApi.ts': {\n      exportName: 'orderApi',\n      filterEndpoints: [/order/i],\n      hooks: true,\n    },\n  },\n};\n\nexport default config;\n```\n\n## Review checklist\n\n- Does every generated endpoint belong in the shared API root?\n- Did any query or mutation type need correction?\n- Are generated tags specific enough for real invalidation needs?\n- Did the schema force parameters into the signature that the app should hide?\n\n<!-- File: packages/rtk-query-codegen-openapi/skills/manage-server-data/generate-rtk-query-from-openapi/SKILL.md (Tokens: ~1504 | Category: anthropic-skill) -->\n---\nname: manage-server-data/generate-rtk-query-from-openapi\ndescription: >\n  Use this when generating RTK Query endpoints from OpenAPI schemas with\n  @rtk-query/codegen-openapi. Covers the empty API pattern, filterEndpoints,\n  endpointOverrides, generated tags, and reviewing generated output before it\n  becomes part of the app.\ntype: composition\nlibrary: '@rtk-query/codegen-openapi'\nlibrary_version: '2.2.0'\nrequires:\n  - manage-server-data/adopt-rtk-query\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/types.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/src/generate.ts'\n  - 'reduxjs/redux-toolkit:packages/rtk-query-codegen-openapi/README.md'\n---\n\n# Generate RTK Query From OpenAPI\n\n## Setup\n\n```ts\n// file: src/store/emptyApi.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n\n// file: openapi-config.ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: 'https://petstore3.swagger.io/api/v3/openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n};\n\nexport default config;\n```\n\nRun:\n\n```bash\nnpx @rtk-query/codegen-openapi openapi-config.ts\n```\n\n## Core Patterns\n\n### Generate into an empty shared API\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nCodegen works best when it extends a single RTK Query architecture instead of creating standalone API roots.\n\n### Filter endpoints when the schema is too broad\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/userApi.ts',\n  exportName: 'userApi',\n  hooks: true,\n  filterEndpoints: ['loginUser', /User/],\n};\n\nexport default config;\n```\n\nStart with a narrow slice of the schema if the full surface area is too noisy or the package boundaries differ from the OpenAPI file.\n\n### Use `endpointOverrides` to fix generation results\n\n```ts\nimport type { ConfigFile } from '@rtk-query/codegen-openapi';\n\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  hooks: true,\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n    {\n      pattern: /.*/,\n      parameterFilter: (_name, parameter) => parameter.in !== 'header',\n    },\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n\nexport default config;\n```\n\nReview generated endpoints and override type, parameter, or tag behavior instead of hand-editing the emitted file.\n\n## Common Mistakes\n\n### HIGH Assuming generated tags are already specific enough\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  tag: true,\n  endpointOverrides: [\n    {\n      pattern: 'getPetById',\n      providesTags: ['SinglePet'],\n    },\n  ],\n};\n```\n\nGenerated tags are string-only by default, so they can invalidate more cache than intended.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### HIGH Generating a brand-new API root instead of extending an empty one\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\ntype Pet = { id: string; name: string };\n\nexport const petApi = createApi({\n  reducerPath: 'petApi',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getPetById: build.query<Pet, string>({\n      query: (id) => `pets/${id}`,\n    }),\n  }),\n});\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';\n\nexport const emptySplitApi = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n});\n```\n\nGenerated code should plug into one RTK Query architecture so invalidation and store wiring stay coherent.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n### MEDIUM Trusting generated shapes without overrides or review\n\nWrong:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n};\n```\n\nCorrect:\n\n```ts\nconst config: ConfigFile = {\n  schemaFile: './openapi.json',\n  apiFile: './src/store/emptyApi.ts',\n  apiImport: 'emptySplitApi',\n  outputFile: './src/store/petApi.ts',\n  exportName: 'petApi',\n  endpointOverrides: [\n    {\n      pattern: 'loginUser',\n      type: 'mutation',\n    },\n  ],\n};\n```\n\nReal schemas often need type, parameter, or tag correction; treat generated output as reviewed source, not gospel.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/code-generation.mdx\n\n## References\n\n- [Codegen override patterns and review checklist](references/codegen-overrides.md)\n\n<!-- File: packages/toolkit/skills/build-modern-redux-apps/modern-redux/references/store-lifetime.md (Tokens: ~355 | Category: anthropic-skill) -->\n# Store Lifetime\n\n## Decision table\n\n| Environment                | Default store shape                     | Why                                                                                                  |\n| -------------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------- |\n| Client-only SPA            | One module-level singleton store        | There is one browser session and no cross-request leakage risk.                                      |\n| SSR-heavy React app        | `makeStore()` plus provider-local state | Each request needs its own store instance, but that instance must stay stable across client renders. |\n| Non-React integration code | Direct store access can be acceptable   | This is outside the React context boundary and should stay out of UI components.                     |\n\n## SPA pattern\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n```\n\nUse this for classic browser SPAs.\n\n## SSR-heavy React pattern\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { postsSlice } from '../features/posts/postsSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      posts: postsSlice.reducer,\n    },\n  })\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCreate the store per request, then keep it stable inside the provider component.\n\n<!-- File: packages/toolkit/skills/build-modern-redux-apps/modern-redux/SKILL.md (Tokens: ~1805 | Category: anthropic-skill) -->\n---\nname: build-modern-redux-apps/modern-redux\ndescription: >\n  Use this when setting up a new Redux Toolkit app or modernizing an existing\n  React + Redux codebase. Covers configureStore, Provider wiring, typed hooks,\n  hooks-first React-Redux usage, feature folders, and the correct store lifetime\n  for SPA and SSR-heavy React environments.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/tutorials/quick-start.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/typescript.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/nextjs.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Modern Redux\n\n## Setup\n\n```tsx\n// file: src/features/counter/counterSlice.ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nexport const counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\nexport const { increment } = counterSlice.actions\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterSlice.reducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\n// file: src/app/hooks.ts\nimport { useDispatch, useSelector } from 'react-redux'\nimport type { AppDispatch, RootState } from './store'\n\nexport const useAppDispatch = useDispatch.withTypes<AppDispatch>()\nexport const useAppSelector = useSelector.withTypes<RootState>()\n\n// file: src/features/counter/Counter.tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n\n// file: src/main.tsx\nimport React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { Counter } from './features/counter/Counter'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n  <Provider store={store}>\n    <Counter />\n  </Provider>,\n)\n```\n\n## Core Patterns\n\n### Keep React components on hooks, not wrappers\n\n```tsx\nimport { postAdded, selectPosts } from './postsSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector(selectPosts)\n  const dispatch = useAppDispatch()\n\n  return (\n    <button\n      onClick={() => dispatch(postAdded({ id: 'p2', title: 'Write docs' }))}\n    >\n      {posts.length}\n    </button>\n  )\n}\n```\n\nHooks are the default React-Redux integration for new code.\n\n### Create the store inside the provider for SSR-heavy React apps\n\n```tsx\n// file: src/lib/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { counterSlice } from '../features/counter/counterSlice'\n\nexport const makeStore = () =>\n  configureStore({\n    reducer: {\n      counter: counterSlice.reducer,\n    },\n  })\n\nexport type AppStore = ReturnType<typeof makeStore>\nexport type RootState = ReturnType<AppStore['getState']>\nexport type AppDispatch = AppStore['dispatch']\n\n// file: src/app/StoreProvider.tsx\n;('use client')\n\nimport { useState, type ReactNode } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({ children }: { children: ReactNode }) {\n  const [store] = useState(makeStore)\n\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nIn SSR-heavy React frameworks, create a store per request and keep that instance stable across renders.\n\n### Keep app wiring in `app/` and feature logic in feature folders\n\n```text\nsrc/\n  app/\n    store.ts\n    hooks.ts\n  features/\n    posts/\n      postsSlice.ts\n      PostsList.tsx\n    users/\n      usersSlice.ts\n      UsersList.tsx\n```\n\nThis keeps store wiring centralized and feature logic colocated.\n\n## Common Mistakes\n\n### HIGH Importing the store in React components\n\nWrong:\n\n```tsx\nimport { store } from '../../app/store'\n\nexport function PostsList() {\n  const posts = store.getState().posts\n  return <div>{posts.length}</div>\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts)\n  return <div>{posts.length}</div>\n}\n```\n\nReact components should read through context and hooks; direct store imports are a separate escape hatch for non-React integrations, not the default UI pattern.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Defaulting to `connect()` in new React code\n\nWrong:\n\n```tsx\nimport { connect } from 'react-redux'\n\nconst increment = () => ({ type: 'counter/increment' as const })\nconst mapState = (state: { counter: { value: number } }) => ({\n  value: state.counter.value,\n})\nconst mapDispatch = { increment }\n\nfunction Counter({\n  value,\n  increment,\n}: ReturnType<typeof mapState> & typeof mapDispatch) {\n  return <button onClick={() => increment()}>{value}</button>\n}\n\nexport default connect(mapState, mapDispatch)(Counter)\n```\n\nCorrect:\n\n```tsx\nimport { increment } from './counterSlice'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nexport function Counter() {\n  const value = useAppSelector((state) => state.counter.value)\n  const dispatch = useAppDispatch()\n\n  return <button onClick={() => dispatch(increment())}>{value}</button>\n}\n```\n\nHooks are the modern default, simpler to type, and the maintainers explicitly want agents to steer new code away from `connect`.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Recreating the store during render in SSR-heavy apps\n\nWrong:\n\n```tsx\n'use client'\n\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const store = makeStore()\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nCorrect:\n\n```tsx\n'use client'\n\nimport { useState } from 'react'\nimport { Provider } from 'react-redux'\nimport { makeStore } from '../lib/store'\n\nexport function StoreProvider({\n  children,\n}: {\n  children: import('react').ReactNode\n}) {\n  const [store] = useState(makeStore)\n  return <Provider store={store}>{children}</Provider>\n}\n```\n\nA new store on every render loses client state, while a module singleton can leak across requests on the server.\n\nSource: reduxjs/redux-toolkit:docs/usage/nextjs.mdx\n\n### HIGH Keeping `createStore` boilerplate as the default\n\nWrong:\n\n```ts\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nconst rootReducer = combineReducers({\n  counter: counterReducer,\n})\n\nexport const store = createStore(rootReducer, applyMiddleware(thunk))\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst counterReducer = (state = { value: 0 }) => state\n\nexport const store = configureStore({\n  reducer: {\n    counter: counterReducer,\n  },\n})\n```\n\nManual store setup works, but it throws away RTK's default middleware, dev checks, and the current recommended baseline.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n## References\n\n- [Store lifetime and framework boundaries](references/store-lifetime.md)\n\n<!-- File: packages/toolkit/skills/build-modern-redux-apps/redux-dataflow/SKILL.md (Tokens: ~1618 | Category: anthropic-skill) -->\n---\nname: build-modern-redux-apps/redux-dataflow\ndescription: >\n  Use this when you need the Redux event -> reducer -> selector -> render loop,\n  event-style actions, reducer-owned state transitions, derived data, or a\n  debugging model for Redux Toolkit apps.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/tutorials/fundamentals/part-2-concepts-data-flow.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-3-data-flow.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Redux Dataflow\n\n## Setup\n\n```ts\nimport { configureStore, createSelector, createSlice } from '@reduxjs/toolkit'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as { id: string; title: string; published: boolean }[],\n    filter: 'all' as 'all' | 'published',\n  },\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.items.push({ ...action.payload, published: false })\n    },\n    postPublished(state, action: { payload: { id: string } }) {\n      const post = state.items.find((item) => item.id === action.payload.id)\n      if (post) {\n        post.published = true\n      }\n    },\n    filterChanged(state, action: { payload: 'all' | 'published' }) {\n      state.filter = action.payload\n    },\n  },\n})\n\nconst store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n\nconst selectPostsState = (state: RootState) => state.posts\nconst selectVisiblePosts = createSelector([selectPostsState], (postsState) =>\n  postsState.filter === 'all'\n    ? postsState.items\n    : postsState.items.filter((post) => post.published),\n)\n\nstore.dispatch(postsSlice.actions.postAdded({ id: 'p1', title: 'Draft' }))\nstore.dispatch(postsSlice.actions.postPublished({ id: 'p1' }))\n\nconst visiblePosts = selectVisiblePosts(store.getState())\nconsole.log(visiblePosts)\n```\n\n## Core Patterns\n\n### Dispatch events, not setters\n\n```ts\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string }[],\n  reducers: {\n    postAdded(state, action: { payload: { id: string; title: string } }) {\n      state.push(action.payload)\n    },\n    postRemoved(state, action: { payload: { id: string } }) {\n      return state.filter((post) => post.id !== action.payload.id)\n    },\n    postUpdated(\n      state,\n      action: { payload: { id: string; changes: Partial<{ title: string }> } },\n    ) {\n      const post = state.find((item) => item.id === action.payload.id)\n      if (post && action.payload.changes.title) {\n        post.title = action.payload.changes.title\n      }\n    },\n  },\n})\n\npostsSlice.actions.postAdded({ id: 'p1', title: 'Draft' })\n```\n\nEvent-style actions explain what happened in the UI instead of hiding the transition behind a generic setter.\n\n### Let reducers combine old store data with new outside data\n\n```ts\nimport { createEntityAdapter, createSlice } from '@reduxjs/toolkit'\n\nconst postsAdapter = createEntityAdapter<{ id: string; title: string }>()\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: postsAdapter.getInitialState(),\n  reducers: {\n    postsReceived(state, action: { payload: { id: string; title: string }[] }) {\n      postsAdapter.upsertMany(state, action.payload)\n    },\n  },\n})\n\nconst incomingPosts = [\n  { id: 'p1', title: 'Draft' },\n  { id: 'p2', title: 'Published' },\n]\n\npostsSlice.actions.postsReceived(incomingPosts)\n```\n\nIf a transition mixes current store state with new external data, dispatch the new external data and let the reducer own the merge.\n\n### Derive values with selectors instead of storing duplicates\n\n```ts\nimport { createSelector } from '@reduxjs/toolkit'\n\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectFilter = (state: RootState) => state.posts.filter\n\nexport const selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nSelectors keep a single source of truth in state while still exposing the shapes the UI needs.\n\n## Common Mistakes\n\n### CRITICAL Mutating selected state outside reducers\n\nWrong:\n\n```ts\nconst post = selectPostById(store.getState(), 'p1')\n\nif (post) {\n  post.title = 'Changed in place'\n}\n```\n\nCorrect:\n\n```ts\nstore.dispatch(\n  postUpdated({ id: 'p1', changes: { title: 'Changed in place' } }),\n)\n```\n\nObjects read from the store are still store state; mutating them outside reducers breaks immutability and stale-render assumptions.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using setter-style actions instead of event-style actions\n\nWrong:\n\n```ts\nconst nextPosts = [\n  ...selectPosts(store.getState()),\n  { id: 'p2', title: 'Write docs' },\n]\nstore.dispatch(setPosts(nextPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postAdded({ id: 'p2', title: 'Write docs' }))\n```\n\nActions should describe events, not ask reducers to blindly replace state with a precomputed value.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Combining store state before dispatch\n\nWrong:\n\n```ts\nconst currentPosts = selectPosts(store.getState())\nconst mergedPosts = [\n  ...currentPosts.filter(\n    (currentPost) =>\n      !incomingPosts.some((incomingPost) => incomingPost.id === currentPost.id),\n  ),\n  ...incomingPosts,\n]\n\nstore.dispatch(postsReplaced(mergedPosts))\n```\n\nCorrect:\n\n```ts\nstore.dispatch(postsReceived(incomingPosts))\n```\n\nIf the next state depends on current store state, the reducer should own that combination logic; only authoritative external snapshots should replace state wholesale.\n\nSource: maintainer interview\n\n### HIGH Ignoring current state in async reducers\n\nWrong:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  state.status = 'succeeded'\n  state.items = action.payload\n})\n```\n\nCorrect:\n\n```ts\nbuilder.addCase(fetchPosts.fulfilled, (state, action) => {\n  if (state.status === 'pending') {\n    state.status = 'succeeded'\n    state.items = action.payload\n  }\n})\n```\n\nReducers that treat every lifecycle action as valid can move the slice into impossible states or let stale requests win.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### MEDIUM Storing derived values in state\n\nWrong:\n\n```ts\nconst initialState = {\n  items: [] as Post[],\n  visiblePosts: [] as Post[],\n}\n```\n\nCorrect:\n\n```ts\nconst selectVisiblePosts = createSelector(\n  [selectPosts, selectFilter],\n  (posts, filter) =>\n    filter === 'all' ? posts : posts.filter((post) => post.published),\n)\n```\n\nDerived values drift out of sync quickly; keep the raw state and derive the view shape.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n<!-- File: packages/toolkit/skills/evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps/SKILL.md (Tokens: ~1566 | Category: anthropic-skill) -->\n---\nname: evolve-and-diagnose-redux-apps/debug-redux-toolkit-apps\ndescription: >\n  Use this when debugging duplicate requests, stale cache behavior, broad\n  subscriptions, selector churn, serializability warnings, or other Redux\n  Toolkit and RTK Query bugs. Covers a practical event -> reducer -> selector ->\n  render debugging loop plus RTK Query cache interpretation.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md'\n  - 'reduxjs/redux-toolkit:docs/usage/usage-guide.md'\n---\n\n# Debug Redux Toolkit Apps\n\n## Setup\n\n```ts\nimport { configureStore, createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: {\n    items: [] as Post[],\n    status: 'idle' as 'idle' | 'pending' | 'succeeded' | 'failed',\n  },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(fetchPosts.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(fetchPosts.fulfilled, (state, action) => {\n        state.status = 'succeeded'\n        state.items = action.payload\n      })\n  },\n})\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsSlice.reducer,\n  },\n})\n\ntype RootState = ReturnType<typeof store.getState>\n```\n\n## Core Patterns\n\n### Debug in order: action -> reducer -> selector -> render\n\n```ts\nconst selectPosts = (state: RootState) => state.posts.items\nconst selectPostsStatus = (state: RootState) => state.posts.status\n\nstore.dispatch(fetchPosts())\n\nconsole.log(selectPostsStatus(store.getState()))\nconsole.log(selectPosts(store.getState()))\n```\n\nIf a component looks wrong, first verify the action fired, then the reducer state, then the selector result, then the render boundary.\n\n### Narrow subscriptions at the usage site\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nexport function PostsList() {\n  const posts = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{posts.length}</div>\n    </div>\n  )\n}\n```\n\nReact-Redux behaves best when components select only the values they render and do it as close to usage as possible.\n\n### Interpret RTK Query invalidation correctly\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf invalidation did not visibly refetch, check whether anything was still subscribed to that cache entry.\n\n## Common Mistakes\n\n### HIGH Dispatching fetch thunks from effects without a thunk-level guard\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch, useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const dispatch = useAppDispatch()\n  const postStatus = useAppSelector((state) => state.posts.status)\n\n  useEffect(() => {\n    if (postStatus === 'idle') {\n      dispatch(fetchPosts())\n    }\n  }, [dispatch, postStatus])\n\n  return null\n}\n```\n\nCorrect:\n\n```ts\nexport const fetchPosts = createAsyncThunk(\n  'posts/fetchPosts',\n  async () => {\n    const response = await fetch('/api/posts')\n    return (await response.json()) as Post[]\n  },\n  {\n    condition(_arg, { getState }) {\n      const state = getState() as RootState\n      return state.posts.status === 'idle'\n    },\n  },\n)\n```\n\nReact StrictMode can run effects twice in development, so the guard belongs in the thunk as well as the component.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md\n\n### HIGH Ignoring serializable-state warnings\n\nWrong:\n\n```ts\nconst initialState = {\n  lastSeen: new Date(),\n  pendingIds: new Set<string>(),\n}\n```\n\nCorrect:\n\n```ts\nconst initialState = {\n  lastSeenIso: new Date().toISOString(),\n  pendingIds: [] as string[],\n}\n```\n\nNon-serializable values break DevTools, replay, persistence, and equality assumptions in subtle ways.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Selecting broad state in parents and threading props\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsPage() {\n  const postsState = useAppSelector((state) => state.posts)\n  return <PostsList items={postsState.items} status={postsState.status} />\n}\n```\n\nCorrect:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nfunction PostsList() {\n  const items = useAppSelector((state) => state.posts.items)\n  const status = useAppSelector((state) => state.posts.status)\n  return (\n    <div>\n      <div>{status}</div>\n      <div>{items.length}</div>\n    </div>\n  )\n}\n```\n\nSelecting whole slices high in the tree widens the subscription surface and pushes rerenders through props.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Returning unstable objects from query selection logic\n\nWrong:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: [...data],\n  }),\n})\n```\n\nCorrect:\n\n```tsx\nimport { api } from '../../services/api'\n\nconst result = api.useGetPostsQuery(undefined, {\n  selectFromResult: ({ data = [] }) => ({\n    posts: data,\n  }),\n})\n```\n\nNew object and array references defeat memoization and make components rerender even when the underlying cached data did not change.\n\nSource: reduxjs/redux:docs/tutorials/essentials/part-8-rtk-query-advanced.md\n\n<!-- File: packages/toolkit/skills/evolve-and-diagnose-redux-apps/migrate-to-modern-redux/SKILL.md (Tokens: ~1448 | Category: anthropic-skill) -->\n---\nname: evolve-and-diagnose-redux-apps/migrate-to-modern-redux\ndescription: >\n  Use this when moving a legacy Redux codebase to current RTK patterns. Covers\n  replacing createStore with configureStore, migrating touched reducers to\n  createSlice, codemod-assisted RTK 2 updates, and replacing server-data stacks\n  with RTK Query instead of writing new legacy Redux code.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux-toolkit:packages/rtk-codemods/README.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Migrate To Modern Redux\n\n## Setup\n\n```ts\n// before\nimport { applyMiddleware, combineReducers, createStore } from 'redux'\nimport thunk from 'redux-thunk'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nconst rootReducer = combineReducers({\n  posts: postsReducer,\n  users: usersReducer,\n})\n\nexport const legacyStore = createStore(rootReducer, applyMiddleware(thunk))\n\n// after\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n```\n\n## Core Patterns\n\n### Replace the store setup first\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\nconst usersReducer = (state = [] as { id: string; name: string }[]) => state\n\nexport const store = configureStore({\n  reducer: {\n    posts: postsReducer,\n    users: usersReducer,\n  },\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n```\n\nThis is the one migration step that can happen immediately while old reducers continue to work.\n\n### Migrate reducers as you touch them\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype TodosState = {\n  items: { id: string; text: string; completed: boolean }[]\n}\n\nconst initialState: TodosState = {\n  items: [],\n}\n\nexport const todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.items.push({ ...action.payload, completed: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.items.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.completed = !todo.completed\n      }\n    },\n  },\n})\n```\n\nOnce a reducer needs editing, migrate that reducer instead of adding more legacy code to it.\n\n### Use codemods for mechanical updates\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder src/features/posts/postsSlice.ts\nnpx @reduxjs/rtk-codemods createReducerBuilder src/features/posts/postsReducer.ts\n```\n\nUse codemods for repetitive RTK API migrations, then review the result and finish the semantic cleanup by hand.\n\n### Replace legacy server-data stacks with RTK Query\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nWhen the old code is just request status plus fetched data, migrate toward RTK Query instead of carrying the thunk stack forward forever.\n\n## Common Mistakes\n\n### HIGH Attempting a big-bang rewrite\n\nWrong:\n\n```ts\n// Replace every reducer, every connected component, and every async flow\n// in one branch before shipping anything.\n```\n\nCorrect:\n\n```ts\n// 1) Switch createStore to configureStore\n// 2) Migrate one touched reducer to createSlice\n// 3) Convert touched connected components to hooks\n// 4) Repeat without introducing new legacy Redux code\n```\n\nModern Redux migration is incremental, but once the store is modernized new work should stop adding legacy patterns.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### CRITICAL Carrying removed RTK 2 config forms forward\n\nWrong:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: [logger],\n})\n```\n\nCorrect:\n\n```ts\nconfigureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),\n})\n```\n\nRTK 2 removed array middleware configuration and other older builder forms that agents trained on RTK 1.x still emit.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Preserving hand-written fetch state by default\n\nWrong:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\ntype Todo = { id: string; text: string }\n\nconst initialState = {\n  items: [] as Todo[],\n  status: 'idle' as 'idle' | 'pending' | 'failed',\n}\n\nexport const fetchTodos = createAsyncThunk('todos/fetch', async () => {\n  const response = await fetch('/api/todos')\n  return (await response.json()) as Todo[]\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Todo = { id: string; text: string }\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: (build) => ({\n    getTodos: build.query<Todo[], void>({\n      query: () => 'todos',\n    }),\n  }),\n})\n```\n\nIf the feature is really server cache, keep the migration moving toward RTK Query instead of rebuilding the old loading-flag architecture in new APIs.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n<!-- File: packages/toolkit/skills/manage-server-data/adopt-rtk-query/references/endpoint-lifecycle.md (Tokens: ~255 | Category: anthropic-skill) -->\n# Endpoint Lifecycle\n\n## Invalidation rule\n\nWhen a mutation invalidates tags:\n\n- active subscribers refetch\n- inactive cache entries are removed\n- removed entries fetch again only when something subscribes later\n\nThat behavior is deliberate; invalidation is not a background \"refresh everything\" switch.\n\n## Document cache tradeoff\n\nRTK Query is a document cache, not a normalized entity graph cache.\n\nUse RTK Query by default when:\n\n- the data comes from request/response APIs\n- document caching is acceptable\n- tag invalidation and endpoint lifecycles solve the problem\n\nReach for a different tool when:\n\n- the real requirement is a normalized graph cache\n- the stack already has a domain-specific normalized client that fits better\n\nIf normalized caching is mandatory and no better library is already in the stack, a slice plus thunk flow may be the fallback.\n\n## Useful endpoint options\n\n- `providesTags`: tell RTK Query what cache entries this query represents\n- `invalidatesTags`: tell RTK Query what a mutation dirties\n- `onQueryStarted`: optimistic and pessimistic updates tied to a request\n- `onCacheEntryAdded`: long-lived subscriptions such as streaming data\n- `keepUnusedDataFor`: how long inactive cache entries stay around\n\n<!-- File: packages/toolkit/skills/manage-server-data/adopt-rtk-query/SKILL.md (Tokens: ~2364 | Category: anthropic-skill) -->\n---\nname: manage-server-data/adopt-rtk-query\ndescription: >\n  Use this when adding RTK Query as the default server-data and document-cache\n  layer. Covers createApi, store integration, hooks, invalidation behavior,\n  optimistic updates, and deciding when RTK Query is the right cache model.\ntype: lifecycle\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/modern-redux\nsources:\n  - 'reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx'\n  - 'reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx'\n  - 'reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Adopt RTK Query\n\n## Setup\n\n```tsx\n// file: src/services/api.ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    addPost: build.mutation<Post, Pick<Post, 'title'>>({\n      query: (body) => ({\n        url: 'posts',\n        method: 'POST',\n        body,\n      }),\n      invalidatesTags: ['Post'],\n    }),\n  }),\n})\n\nexport const { useGetPostsQuery, useAddPostMutation } = api\n\n// file: src/app/store.ts\nimport { configureStore } from '@reduxjs/toolkit'\nimport { api } from '../services/api'\n\nexport const store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n\n// file: src/App.tsx\nimport { Provider } from 'react-redux'\nimport { store } from './app/store'\nimport { useAddPostMutation, useGetPostsQuery } from './services/api'\n\nfunction Posts() {\n  const { data: posts = [] } = useGetPostsQuery()\n  const [addPost] = useAddPostMutation()\n\n  return (\n    <div>\n      <button onClick={() => addPost({ title: 'Write docs' })}>Add</button>\n      <ul>\n        {posts.map((post) => (\n          <li key={post.id}>{post.title}</li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nexport function App() {\n  return (\n    <Provider store={store}>\n      <Posts />\n    </Provider>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep one API slice per base URL and extend it\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n\nexport const postsApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getPosts: build.query<{ id: string; title: string }[], void>({\n      query: () => 'posts',\n    }),\n  }),\n})\n```\n\nSplit files with `injectEndpoints`, not by making multiple `createApi` roots for the same backend.\n\n### Use tags for cache invalidation\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: (result) =>\n        result\n          ? [...result.map(({ id }) => ({ type: 'Post' as const, id })), 'Post']\n          : ['Post'],\n    }),\n    updatePost: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      invalidatesTags: (_result, _error, { id }) => [{ type: 'Post', id }],\n    }),\n  }),\n})\n```\n\nTreat tags as the normal invalidation path before reaching for manual cache patching.\n\n### Do optimistic updates in endpoint lifecycles\n\n```ts\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n    updatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n      query: ({ id, title }) => ({\n        url: `posts/${id}`,\n        method: 'PATCH',\n        body: { title },\n      }),\n      async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n        const patch = dispatch(\n          api.util.updateQueryData('getPosts', undefined, (draft) => {\n            const post = draft.find((item) => item.id === id)\n            if (post) {\n              post.title = title\n            }\n          }),\n        )\n\n        try {\n          await queryFulfilled\n        } catch {\n          patch.undo()\n        }\n      },\n    }),\n  }),\n})\n```\n\nKeep optimistic and pessimistic cache updates inside endpoint lifecycle handlers so they stay coupled to the request.\n\n## Common Mistakes\n\n### CRITICAL Creating multiple API slices for one backend\n\nWrong:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst postsApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype User = { id: string; name: string }\n\nconst baseQuery = fetchBaseQuery({ baseUrl: '/api/' })\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery,\n  endpoints: () => ({}),\n})\n\nconst usersApi = api.injectEndpoints({\n  endpoints: (build) => ({\n    getUsers: build.query<User[], void>({ query: () => 'users' }),\n  }),\n})\n```\n\nOne API slice per base URL preserves invalidation behavior and avoids duplicated middleware work.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/api/createApi.mdx\n\n### HIGH Forgetting `api.reducer` or `api.middleware`\n\nWrong:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore } from '@reduxjs/toolkit'\n\nconst store = configureStore({\n  reducer: {\n    [api.reducerPath]: api.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(api.middleware),\n})\n```\n\nRTK Query hooks need both the reducer and middleware to manage cache state and request lifecycles.\n\nSource: reduxjs/redux-toolkit:docs/tutorials/rtk-query.mdx\n\n### MEDIUM Persisting browser API cache by default\n\nWrong:\n\n```ts\nconst storage = window.localStorage\n\nconst persistConfig = {\n  key: 'root',\n  storage,\n}\n```\n\nCorrect:\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\nconst api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api/' }),\n  endpoints: () => ({}),\n})\n```\n\nPersisting RTK Query cache in browsers often keeps stale data around longer than users expect; treat persistence as a special case, not the default.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/persistence-and-rehydration.mdx\n\n### HIGH Patching cache from components\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst dispatch = useAppDispatch()\n\nuseEffect(() => {\n  dispatch(\n    api.util.updateQueryData('getPosts', undefined, (draft) => {\n      draft.push({ id: 'p3', title: 'Patched from component' })\n    }),\n  )\n}, [dispatch])\n```\n\nCorrect:\n\n```ts\nupdatePostTitle: build.mutation<Post, Pick<Post, 'id' | 'title'>>({\n  query: ({ id, title }) => ({\n    url: `posts/${id}`,\n    method: 'PATCH',\n    body: { title },\n  }),\n  async onQueryStarted({ id, title }, { dispatch, queryFulfilled }) {\n    const patch = dispatch(\n      api.util.updateQueryData('getPosts', undefined, (draft) => {\n        const post = draft.find((item) => item.id === id)\n        if (post) {\n          post.title = title\n        }\n      }),\n    )\n\n    try {\n      await queryFulfilled\n    } catch {\n      patch.undo()\n    }\n  },\n})\n```\n\nComponent-level cache patches drift away from the mutation lifecycle that should own them.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/manual-cache-updates.mdx\n\n### HIGH Expecting invalidation to refetch unsubscribed queries\n\nWrong:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nconst subscription = store.dispatch(api.endpoints.getPosts.initiate())\nsubscription.unsubscribe()\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nCorrect:\n\n```ts\nimport { api } from './api'\nimport { store } from './store'\n\nstore.dispatch(api.endpoints.getPosts.initiate())\nstore.dispatch(api.util.invalidateTags(['Post']))\n```\n\nInvalidation only refetches actively subscribed queries; if no component is using that cache entry, RTK Query drops it and fetches again next time it is needed.\n\nSource: reduxjs/redux-toolkit:docs/rtk-query/usage/automated-refetching.mdx\n\n## References\n\n- [Endpoint lifecycle details and cache tradeoffs](references/endpoint-lifecycle.md)\n\n<!-- File: packages/toolkit/skills/model-redux-state/build-slices-and-selectors/references/slice-patterns.md (Tokens: ~347 | Category: anthropic-skill) -->\n# Slice Patterns\n\n## `getSelectors` for alternate mounting points\n\n```ts\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {},\n  selectors: {\n    selectValue: (state) => state.value,\n  },\n})\n\nexport const { selectValue } = counterSlice.getSelectors(\n  (state: { customCounter: { value: number } }) => state.customCounter,\n)\n```\n\nUse `getSelectors` when the slice is not mounted at its default `reducerPath`.\n\n## `withLazyLoadedSlices` and `injectInto`\n\n```ts\nimport { combineSlices, createSlice, type WithSlice } from '@reduxjs/toolkit'\n\nconst staticSlice = createSlice({\n  name: 'static',\n  initialState: { ready: true },\n  reducers: {},\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices(staticSlice).withLazyLoadedSlices<LazyLoadedSlices>()\n\nconst lazySlice = createSlice({\n  name: 'lazy',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n})\n\ndeclare module '../rootReducer' {\n  export interface LazyLoadedSlices extends WithSlice<typeof lazySlice> {}\n}\n\nexport const injectedLazySlice = lazySlice.injectInto(rootReducer)\n```\n\nThis keeps RootState types aware of reducers that will be injected later.\n\n## Selector caveat\n\nIf a selector depends on caller-specific arguments and must be memoized per caller, prefer a selector factory outside `createSlice.selectors`. `createSlice.selectors` gives you one selector instance, not a selector factory.\n\n<!-- File: packages/toolkit/skills/model-redux-state/build-slices-and-selectors/SKILL.md (Tokens: ~2263 | Category: anthropic-skill) -->\n---\nname: model-redux-state/build-slices-and-selectors\ndescription: >\n  Use this when authoring or refactoring slices with createSlice, selectors,\n  create.asyncThunk, entity adapters, or lazy reducer injection. Covers\n  Immer-backed mutation syntax, slice selectors, getSelectors, injectInto,\n  withLazyLoadedSlices, and current RTK 2 slice patterns.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - model-redux-state/design-state-ownership\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createSlice.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/combineSlices.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx'\n  - 'reduxjs/redux-toolkit:docs/usage/immer-reducers.md'\n  - 'reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n---\n\n# Build Slices And Selectors\n\n## Setup\n\n```ts\n// file: src/app/createAppSlice.ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nexport const createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\n// file: src/features/posts/postsSlice.ts\nimport { createSelector } from '@reduxjs/toolkit'\nimport { createAppSlice } from '../../app/createAppSlice'\n\ntype PostsState = {\n  items: { id: string; title: string; published: boolean }[]\n  status: 'idle' | 'pending' | 'succeeded' | 'failed'\n}\n\nconst initialState: PostsState = {\n  items: [],\n  status: 'idle',\n}\n\nexport const postsSlice = createAppSlice({\n  name: 'posts',\n  initialState,\n  reducers: (create) => ({\n    postAdded: create.reducer<{ id: string; title: string }>(\n      (state, action) => {\n        state.items.push({ ...action.payload, published: false })\n      },\n    ),\n    fetchPosts: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/posts')\n        return (await response.json()) as {\n          id: string\n          title: string\n          published: boolean\n        }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'succeeded'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n  selectors: {\n    selectPosts: (state) => state.items,\n    selectPublishedPosts: createSelector(\n      [(state: PostsState) => state.items],\n      (items) => items.filter((post) => post.published),\n    ),\n  },\n})\n\nexport const { postAdded, fetchPosts } = postsSlice.actions\nexport const { selectPosts, selectPublishedPosts } = postsSlice.selectors\n```\n\n## Core Patterns\n\n### Use mutating logic inside slice reducers\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string; text: string; done: boolean }[],\n  reducers: {\n    todoAdded(state, action: { payload: { id: string; text: string } }) {\n      state.push({ ...action.payload, done: false })\n    },\n    todoToggled(state, action: { payload: { id: string } }) {\n      const todo = state.find((item) => item.id === action.payload.id)\n      if (todo) {\n        todo.done = !todo.done\n      }\n    },\n  },\n})\n```\n\nImmer is the default inside `createSlice`; write the reducer logic directly instead of copying arrays and objects by hand.\n\n### Define selectors in the slice when they belong to the slice\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\nconst counterSlice = createSlice({\n  name: 'counter',\n  initialState: { value: 0 },\n  reducers: {\n    increment(state) {\n      state.value += 1\n    },\n  },\n  selectors: {\n    selectValue: (state) => state.value,\n    selectIsPositive: (state) => state.value > 0,\n  },\n})\n\nconst { selectValue, selectIsPositive } = counterSlice.selectors\n```\n\nSlice selectors keep state-location knowledge next to the slice.\n\n### Use `create.asyncThunk` when the async lifecycle belongs to the slice\n\n```ts\nimport { asyncThunkCreator, buildCreateSlice } from '@reduxjs/toolkit'\n\nconst createAppSlice = buildCreateSlice({\n  creators: { asyncThunk: asyncThunkCreator },\n})\n\nconst usersSlice = createAppSlice({\n  name: 'users',\n  initialState: {\n    items: [] as { id: string; name: string }[],\n    status: 'idle' as 'idle' | 'pending' | 'failed',\n  },\n  reducers: (create) => ({\n    fetchUsers: create.asyncThunk(\n      async () => {\n        const response = await fetch('/api/users')\n        return (await response.json()) as { id: string; name: string }[]\n      },\n      {\n        pending: (state) => {\n          state.status = 'pending'\n        },\n        fulfilled: (state, action) => {\n          state.status = 'idle'\n          state.items = action.payload\n        },\n        rejected: (state) => {\n          state.status = 'failed'\n        },\n      },\n    ),\n  }),\n})\n```\n\nUse this when the async lifecycle handlers naturally live with the slice; otherwise regular `createAsyncThunk` is still fine.\n\n### Use entity adapters and lazy injection for scalable slices\n\n```ts\nimport {\n  combineSlices,\n  createEntityAdapter,\n  createSlice,\n} from '@reduxjs/toolkit'\n\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n\nconst booksSlice = createSlice({\n  name: 'books',\n  initialState: booksAdapter.getInitialState(),\n  reducers: {\n    booksReceived: booksAdapter.setAll,\n  },\n})\n\nexport interface LazyLoadedSlices {}\n\nexport const rootReducer =\n  combineSlices().withLazyLoadedSlices<LazyLoadedSlices>()\n\ndeclare module './rootReducer' {\n  export interface LazyLoadedSlices {}\n}\n\nconst injectedBooksSlice = booksSlice.injectInto(rootReducer)\n\nconst selectors = booksAdapter.getSelectors(\n  (state: ReturnType<typeof rootReducer.selector.original>) =>\n    injectedBooksSlice.selectSlice(state),\n)\n```\n\nEntity adapters standardize normalized collections, and `injectInto` lets a slice stay aware of its injected location.\n\n## Common Mistakes\n\n### CRITICAL Using mutating logic outside slice reducers\n\nWrong:\n\n```ts\ntype Todo = { id: string; text: string }\n\nexport function addTodo(todos: Todo[], todo: Todo) {\n  todos.push(todo)\n  return todos\n}\n```\n\nCorrect:\n\n```ts\ntype Todo = { id: string; text: string }\n\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as Todo[],\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nMutation syntax is only safe inside Immer-backed reducer contexts such as `createSlice` and `createReducer`.\n\nSource: reduxjs/redux-toolkit:docs/usage/immer-reducers.md\n\n### HIGH Writing hand-written switch reducers as the default\n\nWrong:\n\n```ts\nexport default function todosReducer(\n  state = initialState,\n  action: { type: string; payload?: Todo },\n) {\n  switch (action.type) {\n    case 'todos/todoAdded':\n      return state.concat(action.payload as Todo)\n    default:\n      return state\n  }\n}\n```\n\nCorrect:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState,\n  reducers: {\n    todoAdded(state, action: { payload: Todo }) {\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nHand-written reducers are an escape hatch for proven bottlenecks, not the normal thing an agent should generate in RTK code.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-to-modern-redux.mdx\n\n### HIGH Writing RTK 1.x object syntax for `extraReducers`\n\nWrong:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: {\n    [fetchPosts.fulfilled.type]: (state, action) => {\n      state.items = action.payload\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\nconst initialState = { items: [] as { id: string; title: string }[] }\n\nconst fetchPosts = createAsyncThunk('posts/fetch', async () => {\n  const response = await fetch('/api/posts')\n  return (await response.json()) as { id: string; title: string }[]\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState,\n  reducers: {},\n  extraReducers: (builder) => {\n    builder.addCase(fetchPosts.fulfilled, (state, action) => {\n      state.items = action.payload\n    })\n  },\n})\n```\n\nRTK 2 removed the object form; agents trained on RTK 1.x still generate it.\n\nSource: reduxjs/redux-toolkit:docs/usage/migrating-rtk-2.md\n\n### HIGH Assuming `entity.id` exists for every collection\n\nWrong:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>()\n```\n\nCorrect:\n\n```ts\ntype Book = { bookId: string; title: string }\n\nconst booksAdapter = createEntityAdapter<Book>({\n  selectId: (book) => book.bookId,\n})\n```\n\nAdapters default to `entity.id`; collections keyed by another field must provide `selectId`.\n\nSource: reduxjs/redux-toolkit:docs/api/createEntityAdapter.mdx\n\n## References\n\n- [Slice selectors, async creators, and lazy injection details](references/slice-patterns.md)\n\n<!-- File: packages/toolkit/skills/model-redux-state/design-state-ownership/references/state-ownership.md (Tokens: ~262 | Category: anthropic-skill) -->\n# State Ownership Heuristics\n\n## Choose the owner, then the tool\n\n| Kind of state                              | Default owner   | Typical tool                             |\n| ------------------------------------------ | --------------- | ---------------------------------------- |\n| Editable form fields                       | Component       | `useState`                               |\n| Shared mutable app data                    | Redux           | Slice state                              |\n| Server cache                               | RTK Query       | `createApi`                              |\n| URL, pathname, search params               | Router          | Router APIs plus selector inputs         |\n| Browser-only authority like `localStorage` | External source | Read at boundaries, then dispatch events |\n\n## Good reasons to move data into Redux\n\n- Multiple distant parts of the UI need the same mutable data.\n- You need time-travel debugging or a stable action history.\n- The reducer should own transitions because they mix old store state with new inputs.\n\n## Reasons to keep data out of Redux\n\n- Another system already owns it, such as the router.\n- It only matters during editing inside one component tree.\n- It is server cache and RTK Query fits the use case better.\n\n## Re-evaluate slice size\n\n- If data is constantly stitched together outside reducers, it may belong closer together.\n- If unrelated updates keep touching the same slice, split the slice by domain ownership.\n\n<!-- File: packages/toolkit/skills/model-redux-state/design-state-ownership/SKILL.md (Tokens: ~1839 | Category: anthropic-skill) -->\n---\nname: model-redux-state/design-state-ownership\ndescription: >\n  Use this when deciding whether data belongs in Redux, component state, router\n  state, or another external source. Covers state ownership, authority\n  boundaries, slice sizing, and when to move or split data as the app evolves.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nsources:\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-2-app-structure.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-4-using-data.md'\n---\n\n# Design State Ownership\n\n## Setup\n\n```tsx\nimport { useState } from 'react'\nimport { createSlice } from '@reduxjs/toolkit'\nimport { useAppDispatch } from '../../app/hooks'\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as { id: string; title: string; content: string }[],\n  reducers: {\n    postAdded(\n      state,\n      action: { payload: { id: string; title: string; content: string } },\n    ) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst { postAdded } = postsSlice.actions\n\nexport function AddPostForm() {\n  const [title, setTitle] = useState('')\n  const [content, setContent] = useState('')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(postAdded({ id: 'p1', title, content }))\n      }}\n    >\n      <input value={title} onChange={(event) => setTitle(event.target.value)} />\n      <textarea\n        value={content}\n        onChange={(event) => setContent(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\n## Core Patterns\n\n### Keep editable form state local until the user commits it\n\n```tsx\nimport { useState } from 'react'\nimport { useAppDispatch } from '../../app/hooks'\nimport { profileSaved } from './profileSlice'\n\nexport function ProfileForm() {\n  const [displayName, setDisplayName] = useState('Lenz')\n  const dispatch = useAppDispatch()\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault()\n        dispatch(profileSaved({ displayName }))\n      }}\n    >\n      <input\n        value={displayName}\n        onChange={(event) => setDisplayName(event.target.value)}\n      />\n      <button type=\"submit\">Save</button>\n    </form>\n  )\n}\n```\n\nPrefer Redux for shared, durable app state, not every keystroke.\n\n### Keep URL state with the router and combine it at the edge\n\n```tsx\nimport { createSelector } from '@reduxjs/toolkit'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppSelector } from '../../app/hooks'\n\ntype RootState = {\n  posts: {\n    items: { id: string; title: string; published: boolean }[]\n  }\n}\n\nconst selectPosts = (state: RootState) => state.posts.items\n\nconst selectVisiblePosts = createSelector(\n  [selectPosts, (_state: RootState, filter: string) => filter],\n  (posts, filter) =>\n    filter === 'published' ? posts.filter((post) => post.published) : posts,\n)\n\nexport function PostsList() {\n  const [searchParams] = useSearchParams()\n  const filter = searchParams.get('filter') ?? 'all'\n  const posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n\n  return <div>{posts.length}</div>\n}\n```\n\nIf the router already owns a piece of state, pass it into selectors or combine it in the component instead of syncing it into Redux.\n\n### Re-size slices when access patterns change\n\n```ts\nimport { combineReducers, createSlice } from '@reduxjs/toolkit'\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: { items: [] as { id: string; title: string }[] },\n  reducers: {},\n})\n\nexport const rootReducer = combineReducers({\n  auth: authSlice.reducer,\n  posts: postsSlice.reducer,\n})\n```\n\nRevisit slice size over time; unrelated data should split apart, and data constantly stitched together in every component may belong closer together.\n\n## Common Mistakes\n\n### MEDIUM Putting form editing state in Redux\n\nWrong:\n\n```tsx\nimport { useAppSelector } from '../../app/hooks'\n\nconst selectDraftTitle = (state: { draft: { title: string } }) =>\n  state.draft.title\n\nconst title = useAppSelector(selectDraftTitle)\n```\n\nCorrect:\n\n```tsx\nconst [title, setTitle] = useState('')\n\n<input value={title} onChange={(event) => setTitle(event.target.value)} />\n```\n\nPer-keystroke dispatching adds global complexity for data that usually lives in one component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Synchronizing router or URL state into Redux\n\nWrong:\n\n```tsx\nimport { useEffect } from 'react'\nimport { useSearchParams } from 'react-router-dom'\nimport { useAppDispatch } from '../../app/hooks'\n\nfunction PostsPage() {\n  const [searchParams] = useSearchParams()\n  const dispatch = useAppDispatch()\n\n  useEffect(() => {\n    dispatch(filterChanged(searchParams.get('filter') ?? 'all'))\n  }, [dispatch, searchParams])\n\n  return null\n}\n```\n\nCorrect:\n\n```tsx\nconst filter = searchParams.get('filter') ?? 'all'\nconst posts = useAppSelector((state) => selectVisiblePosts(state, filter))\n```\n\nURL state already has an authoritative owner; duplicating it into Redux creates two sources of truth.\n\nSource: maintainer interview\n\n### HIGH Naming state after components\n\nWrong:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst loginReducer = (state = { open: false }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  loginScreen: loginReducer,\n  postsList: postsReducer,\n})\n```\n\nCorrect:\n\n```ts\nimport { combineReducers } from '@reduxjs/toolkit'\n\nconst authReducer = (state = { userId: null as string | null }) => state\nconst postsReducer = (state = [] as { id: string; title: string }[]) => state\n\nconst rootReducer = combineReducers({\n  auth: authReducer,\n  posts: postsReducer,\n})\n```\n\nStore keys should describe data or domain concepts, not the current component tree.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### MEDIUM Letting slice boundaries fossilize\n\nWrong:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\ntype AppNotification = { id: string; message: string }\n\nconst appSlice = createSlice({\n  name: 'app',\n  initialState: {\n    auth: { userId: null as string | null },\n    posts: [] as Post[],\n    notifications: [] as AppNotification[],\n  },\n  reducers: {},\n})\n```\n\nCorrect:\n\n```ts\nimport { createSlice } from '@reduxjs/toolkit'\n\ntype Post = { id: string; title: string }\n\nconst authSlice = createSlice({\n  name: 'auth',\n  initialState: { userId: null as string | null },\n  reducers: {},\n})\n\nconst postsSlice = createSlice({\n  name: 'posts',\n  initialState: [] as Post[],\n  reducers: {},\n})\n```\n\nWhen unrelated data is welded together, every change point gets noisier; split or merge slices as actual access patterns demand.\n\nSource: maintainer interview\n\n### HIGH Blindly spreading payloads into state\n\nWrong:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  return { ...state, ...action.payload }\n}\n```\n\nCorrect:\n\n```ts\nconst state = { id: '1', name: 'Lenz' }\nconst action = { payload: { id: '2', name: 'Mark', ignored: true } }\n\nuserLoggedIn(state, action) {\n  state.id = action.payload.id\n  state.name = action.payload.name\n}\n```\n\nReducers should own the slice shape instead of treating payloads as trusted state patches.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n## References\n\n- [State ownership heuristics](references/state-ownership.md)\n\n<!-- File: packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/references/listener-workflows.md (Tokens: ~241 | Category: anthropic-skill) -->\n# Listener Workflows\n\n## Decision table\n\n| Need                                                       | Reach for                     |\n| ---------------------------------------------------------- | ----------------------------- |\n| Cached server data                                         | RTK Query                     |\n| One imperative async workflow with `dispatch` / `getState` | `createAsyncThunk` or a thunk |\n| React to later actions or state transitions                | `createListenerMiddleware`    |\n\nA good app often mixes imperative and reactive workflows. The split is by job, not by ideology.\n\n## Useful listener helpers\n\n- `predicate`: react to any action when a state condition becomes true\n- `condition`: wait until a condition becomes true before continuing\n- `take`: wait for the next matching action\n- `cancelActiveListeners`: cancel older instances of the same workflow\n- `fork`: start a child task\n\n## Example: cancel stale work\n\n```ts\nstartAppListening({\n  actionCreator: searchRequested,\n  effect: async (action, listenerApi) => {\n    listenerApi.cancelActiveListeners()\n    await listenerApi.delay(250)\n    listenerApi.dispatch(searchStarted(action.payload))\n  },\n})\n```\n\nThis is the kind of long-lived reactive behavior that does not fit a thunk well.\n\n<!-- File: packages/toolkit/skills/orchestrate-side-effects/handle-side-effects/SKILL.md (Tokens: ~1571 | Category: anthropic-skill) -->\n---\nname: orchestrate-side-effects/handle-side-effects\ndescription: >\n  Use this when choosing between RTK Query, createAsyncThunk, handwritten\n  thunks, and createListenerMiddleware. Covers imperative versus reactive\n  workflows, listener middleware setup, and keeping side effects out of\n  reducers and UI components.\ntype: core\nlibrary: '@reduxjs/toolkit'\nlibrary_version: '2.11.2'\nrequires:\n  - build-modern-redux-apps/redux-dataflow\nsources:\n  - 'reduxjs/redux-toolkit:docs/api/createAsyncThunk.mdx'\n  - 'reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx'\n  - 'reduxjs/redux:docs/style-guide/style-guide.md'\n  - 'reduxjs/redux:docs/tutorials/essentials/part-5-async-logic.md'\n---\n\n# Handle Side Effects\n\n## Setup\n\n```ts\nimport {\n  configureStore,\n  createListenerMiddleware,\n  createSlice,\n} from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveStarted(state) {\n      state.status = 'idle'\n    },\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nexport const store = configureStore({\n  reducer: {\n    docs: docsSlice.reducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n\nexport type RootState = ReturnType<typeof store.getState>\nexport type AppDispatch = typeof store.dispatch\n\nexport const startAppListening = listenerMiddleware.startListening.withTypes<\n  RootState,\n  AppDispatch\n>()\n```\n\n## Core Patterns\n\n### Use RTK Query for server cache by default\n\n```ts\nimport { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'\n\ntype Post = { id: string; title: string }\n\nexport const api = createApi({\n  reducerPath: 'api',\n  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),\n  tagTypes: ['Post'],\n  endpoints: (build) => ({\n    getPosts: build.query<Post[], void>({\n      query: () => 'posts',\n      providesTags: ['Post'],\n    }),\n  }),\n})\n```\n\nIf the problem is server data that should be cached and re-used, start with RTK Query instead of a thunk.\n\n### Use `createAsyncThunk` for imperative workflows\n\n```ts\nimport { createAsyncThunk, createSlice } from '@reduxjs/toolkit'\n\ntype Draft = { title: string }\n\nexport const draftSaved = createAsyncThunk(\n  'drafts/save',\n  async (draft: Draft) => {\n    const response = await fetch('/api/drafts', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(draft),\n    })\n\n    return (await response.json()) as { id: string; title: string }\n  },\n)\n\nconst draftsSlice = createSlice({\n  name: 'drafts',\n  initialState: { status: 'idle' as 'idle' | 'pending' | 'failed' },\n  reducers: {},\n  extraReducers: (builder) => {\n    builder\n      .addCase(draftSaved.pending, (state) => {\n        state.status = 'pending'\n      })\n      .addCase(draftSaved.fulfilled, (state) => {\n        state.status = 'idle'\n      })\n      .addCase(draftSaved.rejected, (state) => {\n        state.status = 'failed'\n      })\n  },\n})\n```\n\nUse a thunk when you need one imperative async workflow with `dispatch` and `getState`.\n\n### Use listener middleware for reactive workflows\n\n```ts\nimport { createListenerMiddleware, createSlice } from '@reduxjs/toolkit'\n\nconst docsSlice = createSlice({\n  name: 'docs',\n  initialState: { status: 'idle' as 'idle' | 'saved' },\n  reducers: {\n    saveFinished(state) {\n      state.status = 'saved'\n    },\n  },\n})\n\nconst notificationsSlice = createSlice({\n  name: 'notifications',\n  initialState: [] as string[],\n  reducers: {\n    notificationQueued(state, action: { payload: string }) {\n      state.push(action.payload)\n    },\n  },\n})\n\nconst listenerMiddleware = createListenerMiddleware()\n\nlistenerMiddleware.startListening({\n  actionCreator: docsSlice.actions.saveFinished,\n  effect: async (_action, listenerApi) => {\n    listenerApi.dispatch(\n      notificationsSlice.actions.notificationQueued('Document saved'),\n    )\n  },\n})\n```\n\nListeners fit workflows that react to future actions or state changes over time instead of driving one imperative request from a single callsite.\n\n## Common Mistakes\n\n### CRITICAL Running side effects inside reducers\n\nWrong:\n\n```ts\nconst todosSlice = createSlice({\n  name: 'todos',\n  initialState: [] as { id: string }[],\n  reducers: {\n    todoSaved(state, action: { payload: { id: string } }) {\n      fetch('/api/todos', { method: 'POST' })\n      state.push(action.payload)\n    },\n  },\n})\n```\n\nCorrect:\n\n```ts\nimport { createAsyncThunk } from '@reduxjs/toolkit'\n\nconst todoSaved = createAsyncThunk(\n  'todos/save',\n  async (todo: { id: string }) => {\n    await fetch('/api/todos', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(todo),\n    })\n    return todo\n  },\n)\n```\n\nReducers must stay pure even when Immer is available.\n\nSource: reduxjs/redux:docs/style-guide/style-guide.md\n\n### HIGH Using thunks to watch future state changes\n\nWrong:\n\n```ts\nexport const waitForSave =\n  () =>\n  async (_dispatch: unknown, getState: () => { docs: { status: string } }) => {\n    while (getState().docs.status !== 'saved') {\n      await new Promise((resolve) => setTimeout(resolve, 100))\n    }\n  }\n```\n\nCorrect:\n\n```ts\nstartAppListening({\n  predicate: (_action, currentState) => currentState.docs.status === 'saved',\n  effect: async () => {\n    console.log('Document saved')\n  },\n})\n```\n\nPolling inside thunks fights the architecture; listener middleware is the reactive tool.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n### HIGH Appending listener middleware after the default checks\n\nWrong:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(listenerMiddleware.middleware),\n})\n```\n\nCorrect:\n\n```ts\nimport { configureStore, createListenerMiddleware } from '@reduxjs/toolkit'\n\nconst reducer = (state = { ready: true }) => state\nconst listenerMiddleware = createListenerMiddleware()\n\nconst store = configureStore({\n  reducer,\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().prepend(listenerMiddleware.middleware),\n})\n```\n\nListener add and remove actions may carry functions, so the listener middleware needs to run before serializability checks.\n\nSource: reduxjs/redux-toolkit:docs/api/createListenerMiddleware.mdx\n\n## References\n\n- [Listener helpers and decision heuristics](references/listener-workflows.md)\n\n<!-- File: packages/rtk-codemods/README.md (Tokens: ~254 | Category: plugin-manifest) -->\n# RTK Codemods\n\nA collection of codemods for updating legacy Redux Toolkit API usage patterns to modern patterns.\n\n## Usage\n\nTo run a specific codemod from this project, you would run the following:\n\n```bash\nnpx @reduxjs/rtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs <TRANSFORM NAME> path/of/files/ or/some**/*glob.js\n```\n\n## Transforms\n\n<!--TRANSFORMS_START-->\n\n- [createReducerBuilder](transforms/createReducerBuilder/README.md)\n- [createSliceBuilder](transforms/createSliceBuilder/README.md)\n- [createSliceReducerBuilder](transforms/createSliceReducerBuilder/README.md)\n\n<!--TRANSFORMS_END-->\n\n## Contributing\n\n### Installation\n\n- clone the repo\n- change into the repo directory\n- `pnpm install`\n\n### Running tests\n\n- `pnpm --filter @reduxjs/rtk-codemods test`\n\n<!-- File: packages/rtk-codemods/transforms/createReducerBuilder/README.md (Tokens: ~922 | Category: plugin-manifest) -->\n# createReducerBuilder\n\nRewrites uses of the \"object\" syntax for Redux Toolkit's `createReducer` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\ncreateReducer(initialState, {\n  [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n    // stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded](state: SliceState, action: PayloadAction<string>) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createReducerBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(\n    todoAdded,\n    (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  )\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createReducerBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\ncreateReducer(initialState, {\n  [todoAdded1a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded1b]: (state, action) => action.payload,\n  [todoAdded1c + 'test']: (state, action) => {\n    // stuff\n  },\n  [todoAdded1d](state, action) {\n    // stuff\n  },\n  [todoAdded1e]: function (state, action) {\n    // stuff\n  },\n  todoAdded1f: (state, action) => {\n    //stuff\n  }\n})\n\ncreateReducer(initialState, {\n  [todoAdded2a]: (state, action) => {\n    // stuff\n  },\n  [todoAdded2b](state, action) {\n    // stuff\n  },\n  [todoAdded2c]: function (state, action) {\n    // stuff\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createReducerBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded1a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n  builder.addCase(todoAdded1c + 'test', (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1d, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1e, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded1f, (state, action) => {\n    //stuff\n  })\n})\n\ncreateReducer(initialState, (builder) => {\n  builder.addCase(todoAdded2a, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2b, (state, action) => {\n    // stuff\n  })\n\n  builder.addCase(todoAdded2c, (state, action) => {\n    // stuff\n  })\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n\n<!-- File: packages/rtk-codemods/transforms/createSliceBuilder/README.md (Tokens: ~1068 | Category: plugin-manifest) -->\n# createSliceBuilder\n\nRewrites uses of the `extraReducers` \"object\" syntax for Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax instead, in preparation for removal of the object syntax in RTK 2.0.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```bash\nnpx @reduxjs/rtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\nrtk-codemods createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n\n- [basic-ts](#basic-ts)\n- [basic](#basic)\n\n<!--FIXTURES_TOC_END-->\n\n## <!--FIXTURES_CONTENT_START-->\n\n<a id=\"basic-ts\">**basic-ts**</a>\n\n**Input** (<small>[basic-ts.input.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.input.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n  extraReducers: {\n    [todoAdded]: (state: SliceState, action: PayloadAction<string>) => {\n      // stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n  extraReducers: {\n    [todoAdded](state: SliceState, action: PayloadAction<string>) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic-ts.output.ts](transforms\\createSliceBuilder__testfixtures__\\basic-ts.output.ts)</small>):\n\n```ts\nconst slice1 = createSlice({\n  name: 'a',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState,\n\n  extraReducers: (builder) => {\n    builder.addCase(\n      todoAdded,\n      (state: SliceState, action: PayloadAction<string>) => {\n        // stuff\n      }\n    )\n  }\n})\n```\n\n---\n\n<a id=\"basic\">**basic**</a>\n\n**Input** (<small>[basic.input.js](transforms\\createSliceBuilder__testfixtures__\\basic.input.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n  extraReducers: {\n    [todoAdded1a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded1b]: (state, action) => action.payload,\n    [todoAdded1c + 'test']: (state, action) => {\n      // stuff\n    },\n    [todoAdded1d](state, action) {\n      // stuff\n    },\n    [todoAdded1e]: function (state, action) {\n      // stuff\n    },\n    todoAdded1f: (state, action) => {\n      //stuff\n    }\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n  extraReducers: {\n    [todoAdded2a]: (state, action) => {\n      // stuff\n    },\n    [todoAdded2b](state, action) {\n      // stuff\n    },\n    [todoAdded2c]: function (state, action) {\n      // stuff\n    }\n  }\n})\n```\n\n**Output** (<small>[basic.output.js](transforms\\createSliceBuilder__testfixtures__\\basic.output.js)</small>):\n\n```js\nconst slice1 = createSlice({\n  name: 'a',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded1a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1b, (state, action) => action.payload)\n\n    builder.addCase(todoAdded1c + 'test', (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1d, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1e, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded1f, (state, action) => {\n      //stuff\n    })\n  }\n})\n\nconst slice2 = createSlice({\n  name: 'b',\n  initialState: {},\n\n  extraReducers: (builder) => {\n    builder.addCase(todoAdded2a, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2b, (state, action) => {\n      // stuff\n    })\n\n    builder.addCase(todoAdded2c, (state, action) => {\n      // stuff\n    })\n  }\n})\n```\n\n<!--FIXTURES_CONTENT_END-->\n\n<!-- File: packages/rtk-codemods/transforms/createSliceReducerBuilder/README.md (Tokens: ~266 | Category: plugin-manifest) -->\n# createSliceReducerBuilder\n\nRewrites uses of Redux Toolkit's `createSlice` API to use the \"builder callback\" syntax for the `reducers` field, to make it easier to add prepared reducers and thunks inside of `createSlice`.\n\nNote that unlike the `createReducerBuilder` and `createSliceBuilder` transforms (which both were fixes for deprecated/removed overloads), this is entirely optional. You do not _need_ to apply this to an entire codebase unless you specifically want to. Otherwise, feel free to apply to specific slice files as needed.\n\nShould work with both JS and TS files.\n\n## Usage\n\n```\nnpx @reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n\n# or\n\nyarn global add @reduxjs/rtk-codemods\n@reduxjs/rtk-codemods createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Local Usage\n\n```\nnode ./bin/cli.mjs createSliceReducerBuilder path/of/files/ or/some**/*glob.js\n```\n\n## Input / Output\n\n<!--FIXTURES_TOC_START-->\n<!--FIXTURES_TOC_END-->\n\n<!--FIXTURES_CONTENT_START-->\n<!--FIXTURES_CONTENT_END-->\n\n<!-- File: packages/rtk-query-codegen-openapi/README.md (Tokens: ~213 | Category: plugin-manifest) -->\n<p align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/rtk-incubator/rtk-query/main/logo.png\" width=\"400\" />\n</p>\n<h2 align=\"center\">\nCode Generator\n</h2>\n\n<p align=\"center\">\n   <a href=\"https://discord.gg/0ZcbPKXt5bZ6au5t\" target=\"_blank\">\n    <img src=\"https://img.shields.io/badge/chat-online-green\" alt=\"Discord server\" />\n  </a>\n</p>\n\n### Introduction\n\nThis is a utility library meant to be used with [RTK Query](https://redux-toolkit.js.org/rtk-query/overview) that will generate a typed API client from an OpenAPI schema.\n\n### Documentation\n\n[View the RTK Query Code Generation docs](https://redux-toolkit.js.org/rtk-query/usage/code-generation)\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n\n<!-- File: packages/toolkit/README.md (Tokens: ~1821 | Category: plugin-manifest) -->\n# Redux Toolkit\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/reduxjs/redux-toolkit/tests.yml?style=flat-square)\n[![npm version](https://img.shields.io/npm/v/@reduxjs/toolkit.svg?style=flat-square)](https://www.npmjs.com/package/@reduxjs/toolkit)\n[![npm downloads](https://img.shields.io/npm/dm/@reduxjs/toolkit.svg?style=flat-square&label=RTK+downloads)](https://www.npmjs.com/package/@reduxjs/toolkit)\n\n**The official, opinionated, batteries-included toolset for efficient Redux development**\n\n## Installation\n\n### Create a React Redux App\n\nThe recommended way to start new apps with React and Redux Toolkit is by using [our official Redux Toolkit + TS template for Vite](https://github.com/reduxjs/redux-templates), or by creating a new Next.js project using [Next's `with-redux` template](https://github.com/vercel/next.js/tree/canary/examples/with-redux).\n\nBoth of these already have Redux Toolkit and React-Redux configured appropriately for that build tool, and come with a small example app that demonstrates how to use several of Redux Toolkit's features.\n\n```bash\n# Vite with our Redux+TS template\n# (using the `tiged` tool to clone and extract the template)\nnpx tiged reduxjs/redux-templates/packages/vite-template-redux my-app\n\n# Next.js using the `with-redux` template\nnpx create-next-app --example with-redux my-app\n```\n\nFor React Native, we have [an official Redux+TS template for Expo](https://github.com/reduxjs/redux-templates/tree/master/packages/expo-template-redux-typescript):\n\n```bash\nnpx tiged reduxjs/redux-templates/packages/expo-template-redux-typescript my-app\n```\n\n### An Existing App\n\nRedux Toolkit is available as a package on NPM for use with a module bundler or in a Node application:\n\n```bash\n# NPM\nnpm install @reduxjs/toolkit\n\n# Yarn\nyarn add @reduxjs/toolkit\n```\n\nIf you use an AI agent, run `npx @tanstack/intent@latest install` to install agent skills.\n\nThe package includes a precompiled browser ESM build that can be loaded from a `<script type=\"module\">` tag. No-bundler browser usage also needs an import map, because the build imports Redux Toolkit's dependencies by package name. See the [Getting Started docs](https://redux-toolkit.js.org/introduction/getting-started#an-existing-app) for an example.\n\n## Documentation\n\nThe Redux Toolkit docs are available at **https://redux-toolkit.js.org**, including API references and usage guides for all of the APIs included in Redux Toolkit.\n\nThe Redux core docs at https://redux.js.org includes the full Redux tutorials, as well usage guides on general Redux patterns.\n\n## Purpose\n\nThe **Redux Toolkit** package is intended to be the standard way to write Redux logic. It was originally created to help address three common concerns about Redux:\n\n- \"Configuring a Redux store is too complicated\"\n- \"I have to add a lot of packages to get Redux to do anything useful\"\n- \"Redux requires too much boilerplate code\"\n\nWe can't solve every use case, but in the spirit of [`create-react-app`](https://github.com/facebook/create-react-app), we can try to provide some tools that abstract over the setup process and handle the most common use cases, as well as include some useful utilities that will let the user simplify their application code.\n\nBecause of that, this package is deliberately limited in scope. It does _not_ address concepts like \"reusable encapsulated Redux modules\", folder or file structures, managing entity relationships in the store, and so on.\n\nRedux Toolkit also includes a powerful data fetching and caching capability that we've dubbed \"RTK Query\". It's included in the package as a separate set of entry points. It's optional, but can eliminate the need to hand-write data fetching logic yourself.\n\n## What's Included\n\nRedux Toolkit includes these APIs:\n\n- `configureStore()`: wraps `createStore` to provide simplified configuration options and good defaults. It can automatically combine your slice reducers, add whatever Redux middleware you supply, includes `redux-thunk` by default, and enables use of the Redux DevTools Extension.\n- `createReducer()`: lets you supply a lookup table of action types to case reducer functions, rather than writing switch statements. In addition, it automatically uses the [`immer` library](https://github.com/mweststrate/immer) to let you write simpler immutable updates with normal mutative code, like `state.todos[3].completed = true`.\n- `createAction()`: generates an action creator function for the given action type string. The function itself has `toString()` defined, so that it can be used in place of the type constant.\n- `createSlice()`: combines `createReducer()` + `createAction()`. Accepts an object of reducer functions, a slice name, and an initial state value, and automatically generates a slice reducer with corresponding action creators and action types.\n- `combineSlices()`: combines multiple slices into a single reducer, and allows \"lazy loading\" of slices after initialisation.\n- `createListenerMiddleware()`: lets you define \"listener\" entries that contain an \"effect\" callback with additional logic, and a way to specify when that callback should run based on dispatched actions or state changes. A lightweight alternative to Redux async middleware like sagas and observables.\n- `createAsyncThunk()`: accepts an action type string and a function that returns a promise, and generates a thunk that dispatches `pending/resolved/rejected` action types based on that promise\n- `createEntityAdapter()`: generates a set of reusable reducers and selectors to manage normalized data in the store\n- The `createSelector()` utility from the [Reselect](https://github.com/reduxjs/reselect) library, re-exported for ease of use.\n\nFor details, see [the Redux Toolkit API Reference section in the docs](https://redux-toolkit.js.org/api/configureStore).\n\n## RTK Query\n\n**RTK Query** is provided as an optional addon within the `@reduxjs/toolkit` package. It is purpose-built to solve the use case of data fetching and caching, supplying a compact, but powerful toolset to define an API interface layer for your app. It is intended to simplify common cases for loading data in a web application, eliminating the need to hand-write data fetching & caching logic yourself.\n\nRTK Query is built on top of the Redux Toolkit core for its implementation, using [Redux](https://redux.js.org/) internally for its architecture. Although knowledge of Redux and RTK are not required to use RTK Query, you should explore all of the additional global store management capabilities they provide, as well as installing the [Redux DevTools browser extension](https://github.com/reduxjs/redux-devtools), which works flawlessly with RTK Query to traverse and replay a timeline of your request & cache behavior.\n\nRTK Query is included within the installation of the core Redux Toolkit package. It is available via either of the two entry points below:\n\n```ts no-transpile\nimport { createApi } from '@reduxjs/toolkit/query'\n\n/* React-specific entry point that automatically generates\n   hooks corresponding to the defined endpoints */\nimport { createApi } from '@reduxjs/toolkit/query/react'\n```\n\n### What's included\n\nRTK Query includes these APIs:\n\n- `createApi()`: The core of RTK Query's functionality. It allows you to define a set of endpoints describe how to retrieve data from a series of endpoints, including configuration of how to fetch and transform that data. In most cases, you should use this once per app, with \"one API slice per base URL\" as a rule of thumb.\n- `fetchBaseQuery()`: A small wrapper around fetch that aims to simplify requests. Intended as the recommended baseQuery to be used in createApi for the majority of users.\n- `<ApiProvider />`: Can be used as a Provider if you do not already have a Redux store.\n- `setupListeners()`: A utility used to enable refetchOnMount and refetchOnReconnect behaviors.\n\nSee the [**RTK Query Overview**](https://redux-toolkit.js.org/rtk-query/overview) page for more details on what RTK Query is, what problems it solves, and how to use it.\n\n## Contributing\n\nPlease refer to our [contributing guide](/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Redux Toolkit.\n\n<!-- File: packages/toolkit/scripts/issue-triage/README.md (Tokens: ~400 | Category: plugin-manifest) -->\n# GitHub Issues Triage Tool\n\nA TypeScript-based tool for automatically triaging and categorizing GitHub issues in the Redux Toolkit repository.\n\n## Features\n\n- Fetches open issues from GitHub API\n- Categorizes issues based on labels, titles, and content\n- Generates markdown reports with statistics\n- Caches data to avoid API rate limiting\n- Supports both Node.js and Bun runtimes\n\n## Setup\n\n1. Install dependencies:\n\n```bash\nnpm install\n```\n\n2. Build the TypeScript code:\n\n```bash\nnpm run build\n```\n\n## Usage\n\nRun the triage tool:\n\n```bash\nnpm start\n```\n\nOr for development (builds and runs):\n\n```bash\nnpm run dev\n```\n\n### Using Cached Data\n\nThe tool automatically saves fetched data to `cache/issues-data.json` after each run. To use cached data instead of fetching fresh data (useful for development and testing):\n\n```bash\nnpm start -- --use-cache\n```\n\nOr with the dev command:\n\n```bash\nnpm run dev -- --use-cache\n```\n\n**Note:** The `--use-cache` flag is primarily for development/testing purposes. It loads data from the cache file instantly without making API calls. If the cache file doesn't exist or is corrupted, the tool will automatically fetch fresh data.\n\n## Project Structure\n\n```\nscripts/issue-triage/\n├── src/           # TypeScript source files\n├── dist/          # Compiled JavaScript output\n├── cache/         # Cached JSON data from GitHub\n├── reports/       # Generated triage reports\n├── package.json   # Project dependencies\n└── tsconfig.json  # TypeScript configuration\n```\n\n## Requirements\n\n- Node.js 18+ or Bun 1.0+\n- TypeScript 5.0+\n\n## Development\n\nThis tool is built with TypeScript using ES modules (ESM). The configuration supports both Node.js and Bun runtimes for maximum flexibility.\n</yakaai_skills>"}