Published on May 8, 2025
The React useState
hook adds a state variable to a function component. Setting a state variable gives your component the ability to remember things across page re-renders, so that data can be stored and the component can be updated when the data changes. This guide explains state in React and how to use the useState
hook, with working code examples.
The React JavaScript library provides a set of tools for building user interfaces using components. Each component has a lifecycle in which it is mounted (created and added to the React virtual DOM), updated (re-rendered, which can occur multiple times whenever state or component prop values change, and does not involve a page refresh), and unmounted (destroyed and removed from the virtual DOM). Hooks are functions that tap into this lifecycle so that you can perform actions at different points. React comes with several built-in hooks, and you can also write your own custom hooks.
useState
is one of the hooks that are built into the React library. It is concerned with the management of state: the ability for your React components to store and recall data across their lifecycle.
useState
creates a state variable for storing this data in memory. State data is managed internally by React and does not survive a browser refresh. Multiple useState
hooks can be added to a component to track the value of multiple variables, and when any state variable changes, React automatically triggers a re-render of the component.
You can store objects in a state variable: for example, rather than using multiple useState
hooks to manage the state for an entire HTML form, you can store those values in an object that models the form.
You don’t need state for simple web pages that only display data as they are unconcerned with what the user is doing. Most modern websites and apps, however — even those that may seem to lack interactivity — require state to provide the reactive, engaging experiences users now expect.
Basic functions like remembering a user's preferences (dark or light mode?), pagination, sorting and filtering selections (e.g., in an ecommerce listing), and client-side form validation rely on your code being able to maintain state. You can also use state to perform background tasks like displaying data without having to reload it for each render.
With the useState
hook, React makes state management a straightforward process compared to manually implementing this functionality yourself. React hooks also work with pre-built component libraries.
React also includes the useEffect
, useReducer
, and useContext
hooks to help you effectively manage state for reactive apps, with each serving a different purpose.
The React useEffect
hook can be used in conjunction with useState
to keep local state data in sync with an external source such as an API. This significantly reduces the work required to build frontend applications that must continuously load data from a backend server, like a multiplayer game or chat application.
useEffect
runs just after the component renders, making it suitable for initializing any data that shouldn't block rendering (e.g., setting state, setting up event listeners). You can also use it to return a cleanup function that you can use to tidy up on unmount (e.g., clearing timers).
A reducer is a single function that consolidates all of the state logic for a component (by taking the current state, responding to a specified action, and returning an updated state). The useReducer
hook is an alternative to useState
that sets the reducer function that will handle the state for the React component.
In React, a context allows you to pass data from one component to all components under it, rather than having to pass it from each component down to the next using props (helping you avoid prop drilling). The useContext hook allows you to read the value of the nearest matching context of a parent component. It can be used separately or alongside the useState
hook as they are separate in purpose.
When adding state to a component using the React useState
hook, you must define the name of the state variable and the name of the setState
function that will be used to set/update it (as you are defining these, you choose what to name them). When called, the function to update the state will trigger a re-render if it changes the value of the state variable, determined by React using a shallow comparison.
Below is an example React component for counting sheep that uses useState
to create a sheepCount
state variable with a setSheepCount
function to update it. Note that useState
accepts a value to initialize the state variable with, in this case 0.
When clicked, the HTML button calls the handleButtonClick()
function, which increments the current sheepCount
value and passes it to setSheepCount
to update the state variable. Throughout the example code, the sheepCount
variable can be used to read its current value from the state.
State variables created using the React useState
hook can contain any type of data, from primitives such as strings and numbers to complex nested objects. Note also that state variables are scoped to the component they are declared in.
Here is a more complex example that demonstrates how to use the React useState
hook in a functional application. You can view all of the useState
code examples on this page in this code sandbox to see how they behave when they are running.
The below React component uses the useState
hook to save a list of shopping list items as objects in an array. It uses multiple calls to the React useState
hook to track different elements: one state variable tracks the state of the shoppingListItems
, while the itemName
and itemQuantity
form values have their own state variables that are reset when the form is submitted.
When handling arrays and objects with useState
, it is critical that you never update a value within an array or object — you must always replace it using the setState
function.
As with any programming language feature or development tool, incorrectly using the React useState
hook can lead to errors, unexpected behavior, or degraded performance.
The useState
hook can be used in function components to add state to them, and does not support class components. Hooks must be called at the top level of function components (never inside loops or conditionals!) so that they are always called in the same order, allowing React to maintain a consistent state.
You should also be careful when structuring your code so that you don't create infinite loops when setting state variables, or you will receive the “Too many re-renders” error when a render triggers another set state, which will in turn trigger another render. This commonly happens when setting state for a dependency you have in a useEffect
.
As mentioned before, it's vital to ensure that you do not mutate state values directly, and instead use their associated setState
function. React tracks state by object references, so if you mutate directly, the reference stays the same and React is unable to track it.
If updating an array, you can use the JavaScript spread syntax (...) to duplicate the array and then append, update, or remove values before using the copy to replace the state variable.
If updating multi-dimensional objects or arrays, the spread syntax is insufficient (as it only copies one level deep). A common workaround to this is to use JavaScript’s JSON.parse
and JSON.stringify
methods to create a deep copy of the array or object, for example:
let shoppingListCopy = JSON.parse(JSON.stringify(shoppingList))
This method does struggle with dates and undefined values, so it may be preferable to use the cloneDeep
function from the lodash utility library to accurately clone JavaScript objects if they contain anything more than strings and numbers.
Using a setState
function does not update the state variable for within the current render! This is a gotcha that many new React developers encounter: as useState
is a lifecycle hook, values updated using setState
will not change until the next render. To sidestep this problem, you can create a variable within the current function to hold the state value, modify and use it in the current render, and then finally set it for the next state once it is finalized.
Another common useState
pitfall is that initializers run twice when running in React's strict mode. This is intentional (and only happens in development), and can help ensure that your code is correct, as the initializer running twice should not produce unexpected values if you are properly replacing state variables rather than mutating them directly. As mentioned before, directly mutating state variables should be avoided, and is a mistake that commonly causes frustration, as it means the rendered variable may not update.
You should also consider performance and user experience when designing and implementing state management for your React app. Lazy initialization can help your app load faster while computationally heavy tasks for the initial state are completed.
You can also implement React Suspense to only load components when they are ready for display (it operates completely separately to setState
and other hooks, but can improve the user experience by hiding components until they are ready).
As the state for each instance of a component is isolated to that instance, recreating a component will “reset” it to its default state. This means that if you want to reset the state of a component, you can do this by destroying and recreating it, or by recreating it as a result of conditional rendering. Another way to reset the state of a component is by changing its key. Unintentionally performing these actions can cause the state of a component to unexpectedly reset or not persist, so make sure that you do not recreate components you need to maintain state for, and ensure that you're updating the state variable at the correct point in the component’s lifecycle.
While you can store functions in state variables, you cannot pass the function to the useState
hook directly or React will assume it is an initializer function. To prevent this, you need to add a ()=>
to wrap these functions in another function.
If you are encountering race conditions (where state updates are happening rapidly, and as a result updates may reference old data), but your state isn't complex enough to justify using useReducer
, you can pass a function as an argument to the setState
update function for a state variable so that it always gets the most recent state, no matter what order the updates have been processed in.
If you're bootstrapping a new React project to give the useState hook and state management a try, check out our Contentful React Starter. It includes everything you need to start building a React app with Contentful.
Websites and apps that rely solely on frontend code are limited in scope. Users expect high interactivity reinforced by personalized experiences and dynamic content that cannot be pre-baked into a digital product.
By supporting your React front end with Contentful, you can publish fresh content to your websites, apps, billboards, and other channels with a click. Content curation is handled in our flexible online studio, which you can customize to fit your creative process. You can connect your React apps to Contentful using our REST and GraphQL APIs and JavaScript SDKs, and keep them in sync with React’s useState
and useEffect
hooks for live experiences.
Contentful doesn't just reduce the backend code you need to write and maintain: it serves your structured text, video, and other media content from our high-speed global CDN, providing high performance while also eliminating the need for additional scalable infrastructure for your projects. This leaves you free to focus on building and improving your core experience for your users.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.