useState is a React Hook that lets a component “remember” some information (called state). It returns two values: the current state, and the function you can use to update it.

const [state, setState] = useState(initialState);

Using state

You can add state to your component in three steps:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      You pressed me {count} times
    </button>
  );
}
1
Declare a state variable

Call useState and pass the initial state to it. React will store the state that you passed, and give it back to you.

2
Update state on interaction

To change it, call the state setter function with the next state value. React will put that value into state instead.

3
Render state in the UI

Use the state for rendering by putting it into the JSX.

State is a component’s memory. A state variable lets you hold some information that changes over time and is specific to your component. Here, count holds the number of clicks. However, you can keep any JavaScript value in state—for example, the current input text, the selected gallery image, or the contents of a shopping cart.

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      You pressed me {count} times
    </button>
  );
}

Declaring a state variable

You can declare multiple state variables in a component. You must declare them at the top level of your component, outside of any conditions or loops. This component declares state variables called name and age:

import { useState } from 'react';

function Form() {
  const [name, setName] = useState('Taylor');

  const [age, setAge] = useState(28);

  // ...
1
Current state

You get the latest state value for your JSX.

2
State setter

You get the function that lets you update the state.

3
Initial state

You pass the value you want the state to be initially.

This allows your component to “remember” multiple independent things—for example, different form fields.

import { useState } from 'react';

export default function Form() {
  const [name, setName] = useState('Taylor');
  const [age, setAge] = useState(28);

  return (
    <>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => setAge(age + 1)}>
        Happy birthday!
      </button>
      <p>Hello, {name}. You are {age}.</p>
    </>
  );
}

The [ and ] syntax here is called array destructuring and it lets you read values from an array. The array returned by useState always has exactly two items—it’s a pair. By convention, name them like [thing, setThing].

Gotcha

Calling setState only affects the next render and does not change state in the already running code:

function handleClick() {
  console.log(count);  // 0
  setCount(count + 1); // Request a re-render with 1
  console.log(count);  // Still 0!
}

When not to use it


Updating objects and arrays in state

You can hold objects and arrays in state, too. However, you should always replace objects in state rather than modify the existing ones. Updating objects and updating arrays describe common patterns that help avoid bugs.

import { useState } from 'react';
export default function MovingDot() {
  const [position, setPosition] = useState({
    x: 0,
    y: 0
  });
  return (
    <div
      onPointerMove={e => {
        setPosition({
          x: e.clientX,
          y: e.clientY
        });
      }}
      style={{
        position: 'relative',
        width: '100vw',
        height: '100vh',
      }}>
      <div style={{
        position: 'absolute',
        backgroundColor: 'red',
        borderRadius: '50%',
        transform: `translate(${position.x}px, ${position.y}px)`,
        left: -10,
        top: -10,
        width: 20,
        height: 20,
      }} />
    </div>
  )
}

Try out some recipes

Recipe 1 of 4:
Image gallery

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  let hasPrev = index > 0;
  let hasNext = index < sculptureList.length - 1;

  function handlePrevClick() {
    if (hasPrev) {
      setIndex(index - 1);
    }
  }

  function handleNextClick() {
    if (hasNext) {
      setIndex(index + 1);
    }
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button
        onClick={handlePrevClick}
        disabled={!hasPrev}
      >
        Previous
      </button>
      <button
        onClick={handleNextClick}
        disabled={!hasNext}
      >
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i>
        by {sculpture.artist}
      </h2>
      <h3>
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img
        src={sculpture.url}
        alt={sculpture.alt}
      />
    </>
  );
}

Special cases

Passing the same value to setState

If you pass the current state to setState, React will skip re-rendering the component:

setCount(count); // Won't trigger a re-render

This is a performance optimization. React uses the Object.is() algorithm to compare the values.


Passing an updater function to setState

Instead of passing the next state itself, you may pass a function to setState. Such a function, like c => c + 1 in this example, is called an “updater”. React will call your updater during the next render to calculate the final state.

function handleClick() {
  setCount(123);

  setCount(c => c + 1); // Result: 124
}
1
Setting state with next value

Usually, when you set state, you replace it.

2
Setting state with an updater

But you can pass an updater function to transform it.

Your updater function will receive the pending state and should return the next state.

function handleClick() {
  // 0 => 1
  setCount(c => c + 1);

  // 1 => 2
  setCount(c => c + 1);

  // 2 => 3
  setCount(c => c + 1);
}
1
Pending state

You get the latest state with the previously queued updates applied to it. For example, if count was 0 and you call setCount(c => c + 1) three times in a row, then the pending c state will be 0 in the first updater, 1 in the second one, and 2 in the third one, and 3 is the final state.

2
Next state

You return the next state you want to see on the screen.

Updaters are a bit verbose but sometimes they come in handy. They let you access the pending state rather than the last rendered state. This is helpful if you want to queue multiple updates on top of each other.

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(c => c + 1);
    setCount(c => c + 1);
    setCount(c => c + 1);
  }

  return (
    <>
      <h1>{count}</h1>
      <button onClick={handleClick}>
        Add 3
      </button>
    </>
  )
}

Updater functions run during rendering. This is why they should be pure functions. In other words, your updater functions should only calculate and return the next state. They should not try to “do” things or set state.

If you don’t have a particular reason to use an updater, you can stick with passing the next state directly.

Gotcha

You need to be careful when returning an object from an arrow function. This doesn’t work:

setForm(f => {
  name: f.name.toUpperCase()
});

This code doesn’t work because JavaScript considers => { to be a function body rather than a returned object. Since it is a function body, you have to write an explicit return statement:

setForm(f => {
  return {
    name: f.name.toUpperCase()
  };
});

Alternatively, you have to add parentheses around your object:

setForm(f => ({
  name: f.name.toUpperCase()
}));

This is an unfortunate language quirk of JavaScript, and is not specific to React.

Gotcha

Because setState() with a function argument has this special meaning, you can’t put a function in state like this:

setState(myFunction);

If you really need to put a function in state (which is rare), you can do this instead:

setState(() => myFunction);

Passing an initializer function to useState

The initial state that you pass to useState as an argument is only used for the initial render. For next renders, this argument is ignored. If creating the initial state is expensive, it is wasteful to create and throw it away many times. You can pass a function to useState to calculate the initial state. React will only run it during the initialization.

function TodoList() {
  const [text, setText] = useState('');

  const [todos, setTodos] = useState(
    () => createInitialTodos()
  );

  // ...
1
Initializing state with a value

Most often, you will provide the initial state during render.

2
Initializing state with a function

But you can also give React a function that calculates the initial state instead. React will only call that function when initializing the component, and won’t call it again.

This is a performance optimization. You can use it to avoid creating large objects or arrays on re-renders.

import { useState } from 'react';

function createInitialTodos() {
  const initialTodos = [];
  for (let i = 0; i < 50; i++) {
    initialTodos.push({
      id: i,
      text: 'Item #' + i
    });
  }
  return initialTodos;
}

export default function TodoList() {
  const [todos, setTodos] = useState(
    () => createInitialTodos()
  );

  return (
    <ul>
      {todos.map(item => (
        <li key={item.id}>
          {item.text}
        </li>
      ))}
    </ul>
  );
}

Initializer functions run during rendering. This is why they should be pure functions. In other words, your initializer functions should only calculate and return the initial state. They should not try to “do” things or set state.

If you don’t have a particular reason to use an initializer, you can stick with passing the initial state directly.