Offline Data Sync Queue in React

Offline Data Sync Queue in React

✅ Problem Statement

In web applications, users may lose internet connectivity while performing operations like form submissions or API updates. If not handled gracefully, these actions may fail, leading to data loss or poor user experience.

We need to design an Offline Data Sync Queue in React that:

  • Allows users to continue working even when offline.
  • Queues API requests locally during offline mode.
  • Automatically syncs queued requests to the server when the connection is restored.
  • Provides status indicators for pending and synced requests.


✅ Key Requirements

  • Detect online/offline status reliably.
  • Store unsynced API requests in a local queue (preferably in localStorage).
  • Retry and process the queue automatically when the user comes back online.
  • Show UI feedback (pending, syncing, success, or failure).


✅ Design Overview

Components:

  • NetworkStatusProvider: Listens to online/offline events and shares status across the app.
  • OfflineQueueManager: Handles queue management, storage, and sync logic.
  • DataForm: Example component that submits data and uses the offline queue.
  • QueueDisplay: Shows pending requests (optional).


🗺️ Flow Diagram


Article content
LLD Flow Diagram

📂 File Structure

/components
  ├── NetworkStatusProvider.jsx
  ├── OfflineQueueManager.js
  ├── DataForm.jsx
  └── QueueDisplay.jsx (optional)        

💻 Full React Code


1. 📡 NetworkStatusProvider.jsx

import React, { createContext, useEffect, useState } from 'react';

export const NetworkContext = createContext();

export const NetworkStatusProvider = ({ children }) => {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return (
    <NetworkContext.Provider value={{ isOnline }}>
      {children}
    </NetworkContext.Provider>
  );
};
        

2. 📦 OfflineQueueManager.js

const QUEUE_KEY = 'offlineQueue';

let queue = JSON.parse(localStorage.getItem(QUEUE_KEY)) || [];

export const addToQueue = (request) => {
  queue.push(request);
  localStorage.setItem(QUEUE_KEY, JSON.stringify(queue));
};

export const processQueue = async (isOnline, apiCall) => {
  if (!isOnline || queue.length === 0) return;

  while (queue.length > 0) {
    const request = queue.shift();
    try {
      await apiCall(request);
      console.log('Synced:', request);
    } catch (error) {
      console.error('Failed to sync:', request);
      queue.unshift(request);
      break;
    }
  }

  localStorage.setItem(QUEUE_KEY, JSON.stringify(queue));
};

export const getQueue = () => queue;        

3. 📝 DataForm.jsx

import React, { useContext, useState, useEffect } from 'react';
import { NetworkContext } from './NetworkStatusProvider';
import { addToQueue, processQueue } from './OfflineQueueManager';

const mockApiCall = async (data) => {
  return new Promise((resolve) => setTimeout(() => resolve({ success: true }), 1000));
};

const DataForm = () => {
  const { isOnline } = useContext(NetworkContext);
  const [input, setInput] = useState('');
  const [status, setStatus] = useState('');

  useEffect(() => {
    if (isOnline) {
      processQueue(isOnline, mockApiCall);
    }
  }, [isOnline]);

  const handleSubmit = async (e) => {
    e.preventDefault();
    const requestData = { text: input, timestamp: Date.now() };

    if (isOnline) {
      try {
        await mockApiCall(requestData);
        setStatus('Synced Successfully');
      } catch (err) {
        console.error('API Error, adding to queue.');
        addToQueue(requestData);
        setStatus('Added to Queue');
      }
    } else {
      addToQueue(requestData);
      setStatus('Offline: Added to Queue');
    }

    setInput('');
  };

  return (
    <div className="p-4 max-w-md mx-auto">
      <form onSubmit={handleSubmit} className="space-y-2">
        <input
          type="text"
          className="border border-gray-300 p-2 w-full"
          placeholder="Enter data"
          value={input}
          onChange={(e) => setInput(e.target.value)}
          required
        />
        <button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
          Submit
        </button>
      </form>
      <p className="mt-2 text-sm text-gray-700">{status}</p>
    </div>
  );
};

export default DataForm;
        

4. 📊 QueueDisplay.jsx (Optional)

import React, { useState, useEffect } from 'react';
import { getQueue } from './OfflineQueueManager';

const QueueDisplay = () => {
  const [queue, setQueue] = useState(getQueue());

  useEffect(() => {
    const interval = setInterval(() => setQueue(getQueue()), 1000);
    return () => clearInterval(interval);
  }, []);

  if (queue.length === 0) return null;

  return (
    <div className="p-4 mt-4 border-t">
      <h3 className="font-bold mb-2">Pending Requests:</h3>
      <ul className="list-disc list-inside">
        {queue.map((item, index) => (
          <li key={index}>{item.text}</li>
        ))}
      </ul>
    </div>
  );
};

export default QueueDisplay;
        

5. 🖥️ App.jsx

import React from 'react';
import { NetworkStatusProvider } from './components/NetworkStatusProvider';
import DataForm from './components/DataForm';
import QueueDisplay from './components/QueueDisplay';

function App() {
  return (
    <NetworkStatusProvider>
      <div className="p-4">
        <h1 className="text-2xl font-bold mb-4 text-center">Offline Data Sync Queue</h1>
        <DataForm />
        <QueueDisplay />
      </div>
    </NetworkStatusProvider>
  );
}

export default App;
        

✅ Key Features:

  • 🔌 Network Detection: Real-time online/offline detection.
  • 🗂️ Persistent Queue: Stores unsynced requests in localStorage.
  • 🔁 Auto Retry: Automatically retries queue when back online.
  • 🖥️ User Feedback: Shows success, queued, and syncing statuses.
  • 🔄 Reusability: Can handle any API request format.


📌 Use Cases for Offline Data Sync Queue

1. 📝 Form Submissions in Unstable Networks

Users filling out forms in areas with poor connectivity (like remote locations or while commuting) can continue submitting data without interruption. The form data will be queued and automatically synced once the network is available.


2. 📦 Field Service or Inventory Apps

In warehouse or on-site service apps where technicians or workers may frequently go offline, actions like stock updates, check-ins, or task completions can be safely queued and synced later.


3. 🛒 E-commerce Cart and Orders

Allow users to add items to the cart or place orders even when the connection is lost. The queued actions will be processed as soon as connectivity is restored, preventing frustration and lost sales.


4. ✏️ Content Management Systems (CMS)

Writers and editors working in unstable environments can save drafts, updates, or comments offline. These changes will automatically sync when they regain connectivity.


5. 🚚 Delivery Tracking Apps

Delivery personnel can log deliveries, upload statuses, or mark failed attempts even in offline zones. The queue ensures that updates sync automatically when back online, improving accuracy and timeliness.


🔄 How Offline Data Sync Works After Closing the App

When a user performs actions while offline, the application queues these API requests and stores them in persistent storage such as localStorage or IndexedDB. Unlike in-memory state, this storage is not cleared when the app or browser is closed.

This ensures that even if the user closes the app, the queued requests are safely retained. When the user reopens the app, the application checks the persistent storage on initialization to see if there are any pending requests. If the device is online, the app automatically begins processing the queued requests and syncing them to the server.

If the device is still offline, the queue remains intact and waits until connectivity is restored. Additionally, the application continues to listen for online events using the browser’s navigator.online API, ensuring that the sync can resume as soon as the network is available. This approach guarantees that no data is lost and that offline actions can reliably sync, even across multiple app sessions.


✅ Final Conclusion

The Offline Data Sync Queue in React provides a reliable and user-friendly way to handle offline scenarios in web applications. By intelligently queuing API requests and processing them when the connection is restored, this design improves data reliability, enhances user trust, and ensures seamless application performance across varying network conditions.


Arastu Diwan

SDE-2 Frontend Developer at Coforge - JavaScript | TypeScript | React.js. | Next.js. | React Native

1mo

Insightful

To view or add a comment, sign in

Others also viewed

Explore topics