Updating a nested object
A store keeps a related group of fields under one object and one field must change without losing its siblings.
Zustand · Const
Zustand's set merges only the store's top level. To update nested state safely, copy the nested object and overwrite the changed property; deeper updates require copying each object level involved.
Zustand's set function merges only the top level of the store. When changing a nested object, explicitly copy that object with the spread operator and replace the changed property; for deeper structures, copy every object level involved in the update.
set((state) => ({ nested: { ...state.nested, key: nextValue } }))import { create } from 'zustand'
type Store = {
nested: { count: number }
increment: () => void
}
const useStore = create<Store>((set) => ({
nested: { count: 0 },
increment: () =>
set((state) => ({
nested: {
...state.nested,
count: state.nested.count + 1,
},
})),
}))
useStore.getState().increment()
console.log(useStore.getState().nested.count)The action preserves the nested object while replacing it with a copy containing the incremented count.
A store keeps a related group of fields under one object and one field must change without losing its siblings.
A state value is several object levels deep and must be changed immutably.
A complete replacement state is intentionally constructed and should not be merged with the old store.
Zustand merges updates shallowly, at the store's first level. A nested object therefore needs to be copied explicitly before changing one of its properties.
Follow-up: How would you update a value three levels deep? Copy each containing object with the spread operator, from the outer level down to the object being changed.
Use an updater function such as set((state) => ({ ... })) when the next value depends on current state. For a nested update, spread the existing nested object and calculate the new property from the previous value.
Follow-up: Why prefer an updater function for an increment? It reads the current store value supplied by Zustand and avoids constructing the update from a potentially stale external value.
replace argument to set do?The replace argument can disable Zustand's default merge behavior: set((state) => newState, true). With replacement enabled, the returned object becomes the store state rather than being shallowly merged.
Follow-up: What risk does replacement create? Any state fields omitted from the returned object are no longer present, so the complete state shape must be supplied.
setcreateupdater functionsspread operatordeeply nested object