Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Redux Toolkit · Module

adapter.upsertMany

adapter.upsertMany inserts multiple entities into normalized state and shallowly merges entities whose IDs already exist. It accepts an array or an ID-keyed record.

How it works

upsertMany accepts multiple entities and applies them to a normalized entity state. New IDs are inserted; existing entities receive a shallow merge, so omitted top-level properties remain while nested objects are replaced as whole values.

Syntax

upsertMany<S extends EntityState<T>>(state: S, entities: T[]): S

Parameters

NameTypeDescription
stateS extends EntityState<T>The normalized entity state being updated.
entitiesT[] | Record<EntityId, T>Entities to insert or shallowly merge; this may be an array of T values or a Record<EntityId, T>.

Returns

The updated normalized entity state; the generated case-reducer form returns the adapter-managed state.

Key points

  • The state-level signature accepts an entity state and an array of entities, returning the updated entity state.
  • The operation supports either a list of entities or an ID-keyed record.
  • For an existing ID, it combines old and incoming top-level fields rather than replacing the entire entity.
  • Properties omitted from an incoming existing entity remain unchanged.
  • Entities whose IDs are not present are added to the normalized collection.

Common mistakes

  • Nested incoming values replace the entire existing nested object; this operation does not recursively merge nested data.

Example: Upsert multiple normalized entities

import { createEntityAdapter, createSlice } from '@reduxjs/toolkit'

type Todo = { id: string; title: string; done: boolean }

const todos = createEntityAdapter<Todo>()

const todosSlice = createSlice({
  name: 'todos',
  initialState: todos.getInitialState(),
  reducers: {
    todosUpserted: todos.upsertMany,
  },
})

const state = todosSlice.reducer(undefined, todosSlice.actions.todosUpserted([
  { id: '1', title: 'Read docs', done: false },
  { id: '2', title: 'Ship feature', done: true },
]))

console.log(state.entities['1'])

Creates a normalized entity slice and uses the adapter reducer to insert or shallowly merge multiple todos.

When to use it

Apply a batch synchronization

A normalized API response contains several new records and revisions to existing records.

Handle collection payloads

A slice receives entities from a fulfilled request or websocket batch.

Update flat normalized data

Incoming records have flat top-level fields that can be safely merged.

Interview questions

QWhat does adapter.upsertMany do?

upsertMany handles a collection of entities in normalized state. It inserts entities whose IDs are absent and shallowly merges incoming fields into entities that already exist.

Follow-up: What happens to an existing nested object? The incoming nested value replaces the complete old nested object rather than being recursively merged.

QHow is upsertMany commonly used in a slice?

Its reducer form receives the entity collection through the action payload. The adapter can be used as a case reducer in createSlice, where Redux Toolkit supplies the draft state.

Follow-up: Can the payload be keyed by ID? Yes; the documented API also accepts an object shaped like Record<EntityId, T>.

QHow does upsertMany differ from replacement updates?

Unlike setMany, it preserves existing top-level properties that the incoming entity does not provide. Existing matching fields are overwritten, and new fields are added through the shallow merge.

Follow-up: When is setMany preferable? Use it when each supplied entity should replace its old version completely, including removal of omitted properties.

  • createEntityAdapter
  • EntityState
  • addMany
  • setMany
  • upsertOne
  • updateMany
  • getInitialState

Source: createEntityAdapter. Checked 2026-10-15.