State: A Component's Memory
Components often need to remember something: which image is showing, what's typed in a field, whether a menu is open. React calls this per-component memory state, and you create it with the useState Hook.
When a regular variable isn't enough
The obvious idea, "just use a normal variable", doesn't work, for two separate reasons. First, a local variable doesn't survive a re-render. Every time React renders a component it runs the function again from scratch, so any let index = 0 inside is created fresh and your change from last time is gone. Second, changing a local variable doesn't tell React to re-render. Even if the value somehow stuck around, React has no idea it changed, so it never redraws the screen with the new value.
let index = 0;
function Gallery() {
function handleClick() {
index = index + 1; // changes, but nothing re-renders, and it resets next render
}
// clicking does nothing visible
}
So to update the screen with new data, I need two things at once: to retain the value between renders, and to trigger a re-render so React shows the new value. That pair is exactly what useState gives me.
Adding a state variable
import { useState } from "react";
function Gallery() {
const [index, setIndex] = useState(0);
function handleClick() {
setIndex(index + 1); // stores the new value AND re-renders
}
return <button onClick={handleClick}>Next ({index})</button>;
}
Here index is the current value, kept alive between renders, and setIndex is the setter. Calling it updates the value and asks React to re-render.
Meet your first Hook
useState is a Hook, a special React function whose name starts with use. Hooks let you "hook into" React features like state from inside your components.
Pitfall: Hooks only work at the top level. Any function starting with use can only be called at the top level of a component or of your own Hooks. Never inside conditions, loops, or nested functions.
Why the top level, specifically? Because React matches each Hook to its state by call order, not by name (the array-and-pointer trick in the deep dive below). React only knows "1st useState, 2nd useState, 3rd", so the order has to be identical on every render. The moment a Hook sits inside an if or a loop, the number and order of calls can change between renders. A call that was 2nd last time becomes 1st this time and grabs the wrong pair, silently handing your component someone else's state. That's also why the mental model works: treat Hooks like unconditional declarations of what your component needs, like imports at the top of a file that always run and always in the same spot.
Anatomy of useState
useState returns an array of exactly two items, and I read them with array destructuring:
const [index, setIndex] = useState(0);
// ▲ ▲ ▲
// current setter initial value (used only on the first render)
The argument (0) is the initial value, used only the first time the component renders. After that, useState hands back whatever the current value is.
Note: the naming convention. Name the pair const [something, setSomething], a plain name for the value and set plus the capitalized name for the setter (index / setIndex, showMore / setShowMore). You can name them anything, but this convention makes code readable across every project.
The two-item return uses array destructuring, the same JS syntax as const [a, b] = someArray. See the full useState reference for the API. (The setter is what performs step 1, triggering a render.)
Giving a component multiple state variables
A component can have as many state variables as it needs, of any type. Just call useState multiple times:
const [index, setIndex] = useState(0); // number
const [showMore, setShowMore] = useState(false); // boolean
const [message, setMessage] = useState(""); // string
Rule of thumb: keep unrelated values in separate state variables, and if two always change together, it can be cleaner to combine them into one object (more on that in Choosing the State Structure).
Deep dive: how does React know which state to return? Notice useState gets no name or id telling it which variable it means. So how does the second useState know to return showMore and not index?
The trick is that Hooks rely on a stable call order. Internally React keeps an array of state pairs per component and a pointer that starts at 0 before each render. Every useState call hands back the pair at the current index, then bumps the pointer by one. First call gets pair 0, second call gets pair 1, and so on.
This only works if the calls happen in the same order every render, which is exactly why the "top level only" pitfall exists. Put a Hook inside an if and the order shifts, so React returns the wrong pair. The eslint-plugin-react-hooks linter catches most of these mistakes. For a nice mental model, see React Hooks: Not Magic, Just Arrays.
State is isolated and private
State belongs to a specific instance of a component on screen, not to the component function. Render the same component twice and each copy gets its own, completely independent state. Updating one doesn't touch the other.
<>
<Gallery /> {/* its own index */}
<Gallery /> {/* a separate index, clicking one doesn't move the other */}
</>
State is also private. A parent can't read or change a child's state. If two components need to share a value, the answer isn't to reach into each other, it's to lift the state up to their closest shared parent and pass it down as props (see Sharing State Between Components).
Recap
Use state when a component needs to remember something between renders, and declare it with the useState Hook. Hooks are use-prefixed functions that must be called unconditionally at the top level only, never in conditions, loops, or nested functions, because React matches them to their state by call order. useState returns a pair: the current value and a setter that updates it and triggers a re-render. A component can hold many state variables, and each one is isolated and private to its own instance on screen. To share a value, lift it up to the closest common parent.