Jacob Paris
← Back to all content

Persist data client-side in React with useLocalStorageState

Antonio Stoilkov built an excellent React hook for persisting state in local storage.

It's called useLocalStorageState and it works just like useState, but page navigations and refreshes won't reset it.

Let's look at a simple example of a form that needs a title and description.

  • These are both uncontrolled inputs, so we're using defaultValue to set the initial value.
  • We're also using onChange to track any updates in our state.

If the user refreshes the page before they submit the form, they won't lose any of their work.

import { useLocalStorageState } from "use-local-storage-state"
export default function Page() {
const [title, setTitle] = useLocalStorageState("title", {
defaultValue: "",
const [description, setDescription] =
useLocalStorageState("description", {
defaultValue: "",
return (
onSubmit={(event) => {
onChange={(e) => setTitle(e.target.value)}
onChange={(e) => setDescription(e.target.value)}
<button type="submit">Submit</button>

Now, if you're using SSR, this starts to look a bit janky. The page will render with empty inputs, then update to the values in local storage, causing a Flash of Unstyled Content.

How do we fix that? Hide the form for the first render? Not quite — the server MUST return the form visible in the HTML, or users without javascript won't see it at all.

We need a javascript solution to hide the form on the first render, and then show it on the second render when the local storage values are available. Fortunately for us, suspending premature renders is exactly what React Suspense is designed for.

Simply wrap the form in a Suspense component, and it will only render once the local storage values are available.

Professional headshot

Hi, I'm Jacob

Hey there! I'm a developer, designer, and digital nomad with a background in lean manufacturing.

About once per month, I send an email with new guides, new blog posts, and sneak peeks of what's coming next.

Everyone who subscribes gets access to the source code for this website and every example project for all my tutorials.

Stay up to date with everything I'm working on by entering your email below.

Unsubscribe at any time.