← Back to list

Building an Offline-First Notes App with React

This tutorial explains how to build a Note app in React that implements offline sync: allowing users to continue interacting with the app…

Franklyn Edekobi · 2025-03-26 09:08 · 0 claps · 3.1 min read
#note-app #offline-sync #react #reactjs
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Building an Offline-First Notes App with React

This tutorial explains how to build a Note app in React that implements offline sync: allowing users to continue interacting with the app even when offline and automatically syncing their data when connectivity is restored.

If you haven't, I advise going through this article first to understand the purpose behind this tutorial: ***Building Resilient React Apps: Offline Sync for Unstable Networks***

Don’t worry, I’ll break it down into simple, easy-to-understand steps.

You can find the complete code in the GitHub repository linked below: https://github.com/qobitech/note-app-demo.git

Demo: https://note-app-demo.netlify.app/

Note: The code provided is for demonstration purposes and may need updates for production use. We have kept it simple to help junior developers understand the core concepts.

Overview

This React app allows users to:

  • Write notes with a title and content
  • Save notes both online and offline
  • Automatically sync offline notes when back online
  • Display saved notes and allow users to edit them
  • Delete note(S)

Key Features in the Code

1. State Management

The app uses the useState hook to manage its data:

  • note: Stores the current note being edited.
  • syncInProgress: Tracks whether notes are being synced online.
const [note, setNote] = useState<INote>({ ...defaultNote, id: uuidv4() })
const [syncInProgress, setSyncInProgress] = useState<boolean>(false)

2. Tracking Network Status

A custom hook useNetworkStatus() checks whether the user is online or offline.

const isOnline = useNetworkStatus()

3. Handling Offline Data

A second custom hook useSync() manages offline storage. It provides methods to:

  • Save offline updates (saveOfflineUpdate)
  • Retrieve stored offline notes (getOfflineUpdates)
  • Clear synced offline notes (clearOfflineUpdates)
  • Remove synced offline note item (removeOfflineItem)
const { saveOfflineUpdate, getOfflineUpdates, clearOfflineUpdates } = useSync<INote[]>()

4. Saving Notes

When a note is saved:

  • If online, it attempts to save the note to the server.
  • If offline or saving fails, it stores the note locally.
/**
   * Handles saving a note.
   * - If online, it tries to save the note to the server.
   * - If the request fails,if offline or online, it saves the note locally.
*/
const handleSaveNote = async (id: string, value: string) => {
  if (!value.trim()) return // Prevent saving empty notes

  if (note.id) {
      const filteredNotes = offlineData?.filter((i) => i.id !== note.id) || []
      const updatedNote = { ...note, [id]: value, timeStamp: Date.now() }
      const modifiedNotes = [updatedNote, ...filteredNotes]
      try {
        // save note locally
        await saveOfflineUpdate(modifiedNotes)
        // save note to the server
        if (isOnline) await saveNote(updatedNote) // Try saving online
      } catch (error) {
        console.error('Failed to save note:', error)
        // Optionally add error handling or user notification
      }
  }
}

5. Debouncing User Input

To prevent excessive API calls, the handleDebouncedSaveNote function:

  • Updates the note state immediately
  • Waits 500ms before saving the note
const handleDebouncedSaveNote = (
  e: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>
) => {
  setNote((prev) => ({ ...prev, [e.target.id]: e.target.value }))

  if (debounceTimeout.current) clearTimeout(debounceTimeout.current)

  debounceTimeout.current = window.setTimeout(() => {
    handleSaveNote(e.target.id, e.target.value) // Save after delay
  }, 500)
}

6. Syncing Offline Notes When Online

When the app detects that the user is back online, it:

  1. Retrieves offline notes
  2. Saves them to the server
  3. Clears them from local storage if successful
useEffect(() => {
  if (isOnline) {
    (async () => {
      const offlineNotes = await getOfflineUpdates()
      if (offlineNotes.length > 0) {
        try {
          await Promise.all(offlineNotes.map(saveNote)) // Sync all offline notes
          await clearOfflineUpdates() // Clear after sync
        } catch {
          console.error('Failed to sync offline notes')
        }
      }
    })()
  }
}, [isOnline])

7. Creating & Editing Notes

  • handleNewNote: Creates a new empty note.
  • handleEditNote: Loads an existing note for editing.
const handleNewNote = useCallback(() => {
  setNote({ ...defaultNote, id: uuidv4() })
}, [])

const handleEditNote = useCallback((note: INote) => {
  setNote(note)
}, [])

8. Rendering Notes & UI Components

The app displays saved notes and provides UI for creating/editing them. The SavedNoteItem component renders each saved note.

<SavedNoteGridClass>
  {notes.map((n) => (
    <SavedNoteItem key={n.id} note={n} onClick={() => handleEditNote(n)} />
  ))}
</SavedNoteGridClass>

Summary

This Notes App is designed to work both online and offline, ensuring a seamless experience for users regardless of their internet connection.

Key Takeaways:

  • State Management: Uses useState for tracking notes and sync status.
  • Offline Handling: Saves notes locally when the user is offline.
  • Debouncing: Prevents excessive API calls when typing.
  • Syncing Mechanism: Detects when the user is back online and syncs saved notes.

By understanding these concepts, you can build more resilient applications that work even in unreliable network conditions!


메타데이터
post_id
df2d92e7a6c7
slug
building-an-offline-first-notes-app-with-react-df2d92e7a6c7
url
https://medium.com/@edekobifrank/building-an-offline-first-notes-app-with-react-df2d92e7a6c7
canonical_url
https://medium.com/@edekobifrank/building-an-offline-first-notes-app-with-react-df2d92e7a6c7
author_url
https://medium.com/@edekobifrank
status
ok
fetched_at
2026-07-11 04:13:04