How to Create a To-Do List App Using React and Local Storage

Creating a to-do list app using React and local storage is a practical way to learn essential React concepts such as components, state management, and the useEffect hook, while also understanding how to persist data across browser sessions. This article walks through the process of designing and coding a simple yet fully functional to-do list app that stores user tasks locally on their device.

Why Use React and Local Storage?

React is a popular JavaScript library for building user interfaces. Its component-based architecture makes it easy to build dynamic and interactive applications. On the other hand, local storage is a Web API that allows developers to store key-value pairs in a user’s browser without any expiration. This makes it ideal for simple apps that don’t require a back-end server.

By using both technologies, developers can create responsive applications that load quickly and work offline, providing a seamless user experience.

Setting Up the Environment

First, ensure that Node.js and npm are installed on your system. Then, create a new React app using the following command:

npx create-react-app todo-list-app

Once the setup is complete, navigate into your project folder:

cd todo-list-app

Start the development server:

npm start

Creating the Components

The to-do list application will consist of the following components:

  • App – The main component that holds the entire application
  • TodoList – Displays the list of tasks
  • TodoItem – Represents a single task item
  • AddTodo – Contains the form to add new tasks

App Component

This is the root component of the app. It manages the state for all the to-dos and handles lifecycle events:

import React, { useState, useEffect } from 'react';
import AddTodo from './AddTodo';
import TodoList from './TodoList';

function App() {
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    const storedTodos = JSON.parse(localStorage.getItem('todos'));
    if (storedTodos) {
      setTodos(storedTodos);
    }
  }, []);

  useEffect(() => {
    localStorage.setItem('todos', JSON.stringify(todos));
  }, [todos]);

  const addTodo = (text) => {
    setTodos([...todos, { text, completed: false }]);
  };

  const toggleTodo = (index) => {
    const newTodos = [...todos];
    newTodos[index].completed = !newTodos[index].completed;
    setTodos(newTodos);
  };

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

  return (
    <div className="App">
      <h1>My To-Do List</h1>
      <AddTodo addTodo={addTodo} />
      <TodoList 
        todos={todos} 
        toggleTodo={toggleTodo} 
        deleteTodo={deleteTodo} 
      />
    </div>
  );
}

export default App;

AddTodo Component

This component provides an input field and a button to add new items.

import React, { useState } from 'react';

function AddTodo({ addTodo }) {
  const [value, setValue] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!value.trim()) return;
    addTodo(value);
    setValue('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input 
        type="text" 
        value={value} 
        onChange={(e) => setValue(e.target.value)}
        placeholder="Add a task"
      />
      <button type="submit">Add</button>
    </form>
  );
}

export default AddTodo;

TodoList and TodoItem Components

The TodoList loops over all tasks and renders a TodoItem for each one.

import React from 'react';
import TodoItem from './TodoItem';

function TodoList({ todos, toggleTodo, deleteTodo }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <TodoItem 
          key={index} 
          todo={todo} 
          index={index} 
          toggleTodo={toggleTodo} 
          deleteTodo={deleteTodo} 
        />
      ))}
    </ul>
  );
}

export default TodoList;
import React from 'react';

function TodoItem({ todo, index, toggleTodo, deleteTodo }) {
  return (
    <li style={{ textDecoration: todo.completed ? 'line-through' : '' }}>
      {todo.text}
      <button onClick={() => toggleTodo(index)}>Toggle</button>
      <button onClick={() => deleteTodo(index)}>Delete</button>
    </li>
  );
}

export default TodoItem;

Enhancing the User Experience

Once the basic structure is in place, consider enhancing the UI using CSS or adding animations. Using tools like TailwindCSS or Bootstrap can significantly improve the look and feel of your app.

Another good improvement would be to add a filter that lets users view items that are completed or still pending.

Persisting Data with Local Storage

The use of useEffect ensures that the application data is synced to local storage every time the state changes. That way, a refresh or browser close won’t result in losing the task list.

It’s important to remember that local storage stores data as strings. Hence, data must be serialized using JSON.stringify before saving and parsed back using JSON.parse when retrieving.

Conclusion

Developing a to-do list app using React and local storage is an excellent project for practicing modern frontend development skills. It allows developers to explore concepts like component composition, state management, and data persistence, all while creating a useful application.

This project can also serve as a foundation. Developers may expand it by integrating backend services, useContext, Redux, or even advanced features like drag-and-drop, reminders, or deadline alerts.

Frequently Asked Questions

  • Q: Does this app work without an internet connection?
    A: Yes, because the app uses local storage, it works entirely in the browser without needing a network after the initial load.
  • Q: What happens to the tasks if I close the browser?
    A: Since tasks are saved in local storage, they will persist between sessions unless the user clears their browser cache.
  • Q: Is local storage secure?
    A: Local storage is not encrypted and can be accessed through browser developer tools. It’s not recommended for storing sensitive data.
  • Q: Can I deploy this app online?
    A: Yes, the app can be deployed using platforms like Netlify, Vercel, or GitHub Pages.
  • Q: How can I add categories or deadlines to tasks?
    A: You can modify the task object structure to include additional fields like category and dueDate, and adjust your components accordingly.