useState is the hook that lets a component remember something between renders: a count, the text in an input, whether a menu is open. Every time you call its setter, React renders the component again with the new value.
Click the button, then change useState(0) to useState(10) in the editor. The preview rebuilds and the count starts from 10.
The syntax
const [value, setValue] = useState(initialValue);
initialValueis used on the first render only. On every later render React ignores it and hands back the value it stored.valueis the current value for this render.setValuereplaces the value and schedules a new render.
The names are yours. useState returns a two-item array and the square brackets unpack it, so [isOpen, setIsOpen] and [email, setEmail] are the same pattern.
Call useState at the top level of your component, never inside a loop, a condition or a nested function. React matches each call to its stored value by the order of the calls, so that order must be the same on every render (see the rules of hooks).
Several pieces of state
A component can call useState as many times as it needs. Each call is its own independent value.
State is a snapshot
The setter does not change the variable in your hands. It tells React what the value should be in the next render. Until that render happens, the current one keeps seeing the old value.
Open the console under the preview and click once. The handler logs 0, the old value, and then the component logs a new render with 1. Nothing is wrong: count is a constant for the whole render, and the next render gets a fresh one.
Updater functions
Because of the snapshot, calling the setter three times with count + 1 adds one, not three: every call computes 0 + 1. Pass a function instead and React feeds each call the result of the previous one.
Use the updater form whenever the next value depends on the previous one and the update might not run straight from the latest render: several sets in one handler, a setInterval callback, or an effect.
Objects and arrays in state
React decides whether to render again by comparing the old and new value with Object.is. If you change an object in place and pass the same object back, the comparison says nothing changed and the screen stays as it was. Always pass a new object or array.
{ ...user, age: user.age + 1 } copies every field and overrides one. [...tags, 'code'] adds an item, and filter removes one, both returning a new array. The page on updating arrays and objects covers nested objects, editing one item in a list and inserting in the middle.
Lazy initial state
The argument to useState is evaluated on every render, even though React only uses it the first time. When computing it is expensive (reading localStorage, parsing a big string), pass a function. React calls it once, on the first render.
// Runs readSavedNotes() on every render, then throws the result away
const [notes, setNotes] = useState(readSavedNotes());
// Runs it once
const [notes, setNotes] = useState(readSavedNotes);
const [notes, setNotes] = useState(() => readSavedNotes());
Resetting state with a key
State belongs to a component's position in the tree. If you render a different key in the same place, React treats it as a new component and its state starts over. This is the cleanest way to reset a form when the thing it edits changes.
Type a message, then switch the recipient: the draft clears. Delete key={to} and try again, and the text stays, because React now sees the same Draft in the same place.
Common mistakes
Logging state right after setting it. You see the old value, as shown above. Log in the component body, or compute the next value into a variable first and use that.
Mutating state. user.age++; setUser(user) passes the same object, so nothing renders. Copy first.
Calling the setter during render. <button onClick={setCount(count + 1)}> calls setCount while rendering, which renders again, which calls it again. React stops this loop with "Too many re-renders". Pass a function: onClick={() => setCount(count + 1)}.
Storing values you can compute. If fullName is always first + ' ' + last, compute it during render instead of keeping a third piece of state that can fall out of sync.
Frequently Asked Questions
What does useState return?
An array with exactly two items: the current value and a function that replaces it. You name them yourself with array destructuring, by convention [thing, setThing].
Why does my state not update immediately?
Calling the setter does not change the variable you already have. It asks React to render the component again, and the new value appears in that next render. Inside the current event handler the old value is still in scope, so console.log(count) right after setCount(count + 1) prints the old number.
When should I pass a function to the setter?
When the new value depends on the old one and you set it more than once, or from inside a timer or an effect. setCount(c => c + 1) always starts from the latest value, while setCount(count + 1) starts from the value this render saw.
Can useState hold an object or an array?
Yes, but treat it as read only. Build a new object or array and pass that to the setter (setUser({ ...user, name }), setItems([...items, item])). Changing the existing one in place does not trigger a render.
What is the difference between useState and useReducer?
Both store state. useState suits a few independent values. useReducer moves the update logic into one function that receives an action, which reads better when many events change the same state in related ways.