Stop 08 · ReactProjects · 2/6

Notes App

Part of the Frontend Developer Roadmap

In short

A notes app looks simple, but it teaches the full CRUD loop (create, read, update, delete), plus making data survive a refresh.

A CRUD notes app with create, edit, delete and search — a compact project that exercises component composition and state updates cleanly.

The jaadu mascot scribbles doodles and tick marks on the back page of a school notebook.

Think of it like this

The back pages of your register

Every student scribbles reminders at the back of a notebook: add a line, cross one out, fix a spelling. Your app does the same, except it also has to remember everything after the notebook, your browser tab, is closed and reopened.

Watch it happen

Life of a new note

Editor
useNotes
localStorage
Note list

add("Buy chart paper")

1/5You type a note and press Add. The editor calls add from useNotes.

The big ideas

What you need to know

Full CRUD

Add, list, edit and delete notes, all from one notes array in state.

Persist to localStorage

Save the array whenever it changes and load it on start, so notes survive refreshes.

Search and sort

Filter notes with a search box and show the most recently edited first.

Stable IDs

Give every note an id from crypto.randomUUID() so edits and deletes always hit the right one.

How it works

Build plan

  1. 1

    Add and list

    An input and a button that add new notes to state and show them.

  2. 2

    Edit and delete

    Buttons on each note that update the array without mutating it.

  3. 3

    Persist

    Load from and save to localStorage with a custom hook.

  4. 4

    Polish

    Add search, timestamps and a friendly empty-state message.

See it in code

Walk through the code

ts
1import { useState } from "react";2 3type Note = { id: string; text: string; updatedAt: number };4 5export function useNotes() {6  const [notes, setNotes] = useState<Note[]>([]);7 8  const add = (text: string) =>9    setNotes((n) => [{ id: crypto.randomUUID(), text, updatedAt: Date.now() }, ...n]);10  const update = (id: string, text: string) =>11    setNotes((n) => n.map((x) => (x.id === id ? { ...x, text, updatedAt: Date.now() } : x)));12  const remove = (id: string) => setNotes((n) => n.filter((x) => x.id !== id));13 14  return { notes, add, update, remove };15}

1/4Each note has a stable id, its text and when it was last edited.

All three updates build new arrays instead of changing the old one, so React always re-renders. Swap useState for a useLocalStorage hook to keep notes after a refresh.

Save yourself the pain

Common mistakes

Oops

Using the array index as a note's id or key.

Do this instead

Indexes shift when you delete a note, so the wrong one gets edited. Use crypto.randomUUID().

Oops

Saving to localStorage separately inside every button handler.

Do this instead

Save in one useEffect that watches notes. One place, no forgotten saves.

Your mission

First milestone: notes that survive a refresh

~35 min

Quick check

Your note list uses the array index as key. You delete note 2 of 5. What can go wrong?

Go deeper

  1. 1Model notes as an array of objects in state, with unique IDs.
  2. 2Add search/filter functionality over the notes list.
  3. 3Persist notes to localStorage or a simple backend API.

More projects at this stop

Stuck on this topic? Ask an Insider

Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.

Download
InsideEdge