VOOZH about

URL: https://blog.logrocket.com/web-storage-made-simple-use-local-storage-state/

⇱ Web storage made simple with use-local-storage-state - LogRocket Blog


2020-05-22
1078
#react
John Au-Yeung
18838
πŸ‘ Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

The use-local-storage-state package makes it easier to persist data in the browser by abstracting the use of localStorage into a React Hook. It can save data and parse them automatically, and we can use it in multiple places in our app.

πŸ‘ Web Storage Made Simple With Use-local-storage-state

In this post, we’ll look at how to use the Hook to help us to more simply use localStorage in our React apps.

πŸš€ Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Getting started

To get started, we install the package by writing:

npm install use-local-storage-state

Then we can use it as follows:

import React, { useEffect } from "react";
import useLocalStorageState from "use-local-storage-state";

export default function App() {
 const [todos, setTodos] = useLocalStorageState("todos", []);

 useEffect(() => {
 setTodos(["eat", "drink"]);
 }, []);

 return (
 <div className="App">
 {todos.map((t, i) => (
 <p key={i}>{t}</p>
 ))}
 </div>
 );
}

We imported the useLocalStorageState Hook from use-local-storage-state, then put the Hook in our App component. To set the values of the component when the component loads, we use the useEffect Hook with an empty array in the second argument so that it only loads when App first renders. Once we do that, todos has the values from the Hook, and we can render them in the div we return.

If we look at the localStorage section of our browser console, which is under the Application tab of the Chrome console, we can see that the key is todos, which we set as the first argument of useLocalStorageState. Its value, ["eat", "drink"], is the array we called with setTodo. The Hook stringified the value for us without our using JSON.stringify, which is convenient.

Update localStorage

To update the localStorage value, we can write the following code:

import React, { useEffect, useState } from "react";
import useLocalStorageState from "use-local-storage-state";

export default function App() {
 const [todos, setTodos] = useLocalStorageState("todos", []);
 const [todo, setTodo] = useState("");

 const onClick = () => {
 setTodos([...todos, todo]);
 setTodo("");
 };

 useEffect(() => {
 setTodos(["eat", "drink"]);
 }, []);

 return (
 <div className="App">
 <input value={todo} onChange={e => setTodo(e.target.value)} />
 <button onClick={onClick}>Create</button>
 {todos.map((t, i) => (
 <p key={i}>{t}</p>
 ))}
 </div>
 );
}

We added an input element and an onClick handler to add entries to the todos array. This will automatically be saved in localStorage since we called the setTodos function returned by useLocalStorageState.

Deleting items

To delete a to-do item, we can write:

import React, { useEffect, useState } from "react";
import useLocalStorageState from "use-local-storage-state";

export default function App() {
 const [todos, setTodos] = useLocalStorageState("todos", []);
 const [todo, setTodo] = useState("");

 const onClick = () => {
 setTodos([...todos, todo]);
 setTodo("");
 };

 const onDelete = index => {
 const newTodos = todos.filter((_, i) => i !== index);
 setTodos(newTodos);
 };

 useEffect(() => {
 setTodos(["eat", "drink"]);
 }, []);

 return (
 <div className="App">
 <input value={todo} onChange={e => setTodo(e.target.value)} />
 <button onClick={onClick}>Create</button>
 {todos.map((t, i) => (
 <p key={i}>
 {t} <button onClick={onDelete.bind(undefined, i)}>Delete</button>
 </p>
 ))}
 </div>
 );
}

We added an onDelete function that calls filter on the todo array to return a new array without the specified index. We then pass the newTodos array that’s returned to setTodos.

To get the index to the function, we have onDelete.bind(undefined, i) in our onClick for the Delete buttons. bind returns a new function that has the arguments we want passed in, which, in this case, would be the index of the entry we want to delete.

Editing an item and using the same Hook in multiple places

To add edit functionality, we want to create a component to both display the value and to let us perform the edits. To do that, we write:

import React, { useEffect, useState } from "react";
import { createLocalStorageStateHook } from "use-local-storage-state";

const useTodos = createLocalStorageStateHook("todos");

const Todo = ({ name, index, onDelete }) => {
 const [todos, setTodos] = useTodos();
 const [todo, setTodo] = useState("");
 const [editing, setEditing] = useState(false);

 const save = () => {
 const newTodos = [...todos];
 newTodos[index] = todo;
 setTodos(newTodos);
 setEditing(false);
 };

 useEffect(() => {
 setTodo(name);
 }, []);

 return (
 <div>
 {(() => {
 if (editing) {
 return (
 <p>
 <input value={todo} onChange={e => setTodo(e.target.value)} />
 <button onClick={save}>Save</button>
 </p>
 );
 } else {
 return <p>{name}</p>;
 }
 })()}
 <button onClick={() => setEditing(editing => !editing)}>Edit</button>
 <button onClick={onDelete}>Delete</button>
 </div>
 );
};

export default function App() {
 const [todos, setTodos] = useTodos();
 const [todo, setTodo] = useState("");

 const onClick = () => {
 setTodos([...todos, todo]);
 setTodo("");
 };

 const onDelete = index => {
 const newTodos = todos.filter((_, i) => i !== index);
 setTodos(newTodos);
 };

 return (
 <div className="App">
 <input value={todo} onChange={e => setTodo(e.target.value)} />
 <button onClick={onClick}>Create</button>
 {todos.map((t, i) => (
 <Todo key={i} index={i} name={t} onDelete={onDelete.bind(this, i)} />
 ))}
 </div>
 );
}

In the code above, we created a Todo component to hold the to-do item with an input that allows us to edit the to-do. We also called the createLocalStorageStateHook function so that we can share the localStorage content in both Todo and App. In Todo and App, we use the useTodo Hook that’s returned by createLocalStorageStateHook.



In the save of Todo, we make a copy of the todos array and set the current value of todo according to the index value. We then call setTodo returned by useTodos to save the new todo item. Calling setEditing with false disables the input.

The onDelete function in Todo comes from App. We call bind on onDelete, then pass it in as the value of the onDelete so that it’s called with the right index. We can now click Edit, enter the new to-do value, and save the new value to localStorage.


Over 200k developers use LogRocket to create better digital experiences

πŸ‘ Image
Learn more β†’

Also, note that we removed the useEffect Hook in App so that it retrieves the items from localStorage instead of resetting them with the value in that Hook. Now we get a complete to-do app that we can add, edit, and delete items with.

Conclusion

The use-local-storage-state package gives us an easy way to use localStorage in React apps. With it, we can create, update, and delete data with ease.

With this, we no longer need to use JSON.parse or JSON.stringify to deal with localStorage values. All we have to do is call the functions returned by the built-in Hooks, which save the values for us.

Get set up with LogRocket's modern React error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
πŸ‘ Image
πŸ‘ Image
πŸ‘ Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

πŸ‘ Image
Emmanuel John
Jun 17, 2026 β‹… 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

πŸ‘ Image
Chizaram Ken
Jun 16, 2026 β‹… 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension β€” no new framework required.

πŸ‘ Image
Ikeh Akinyemi
Jun 12, 2026 β‹… 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo β€” with email/password login, Google OAuth, session persistence, and protected routes.

πŸ‘ Image
Chinwike Maduabuchi
Jun 9, 2026 β‹… 13 min read
View all posts

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now