Combining Sync Strategies for Seamless User Experience

To create a truly offline-first experience, we need to combine Background Sync and IndexedDB to ensure users never lose data.

1. Syncing Data When the User Returns Online

A common approach is to listen for network status changes and sync data automatically:

window.addEventListener("online", async () => {
  console.log("Back online! Syncing data...");
  await syncFormData();
});

This ensures that any pending actions are completed as soon as an internet connection is restored.


2. Implementing a Cache-First Strategy for Faster Data Access

Instead of always making network requests, we can cache API responses and serve them from IndexedDB when offline:

async function fetchWithCache(url) {
  try {
    const response = await fetch(url);
    const data = await response.json();
    await saveDataLocally(url, data);
    return data;
  } catch (error) {
    console.log("Fetching from IndexedDB:", url);
    return await getDataFromIndexedDB(url);
  }
}

This technique ensures:


3. Handling Conflict Resolution for Offline Changes

If a user makes edits while offline, we must handle conflicts when syncing back to the server. One approach is versioning:

Example Rails model with versioning:

class Post < ApplicationRecord
  validates :updated_at, presence: true
end

And in the client, we send the timestamp with updates:

async function syncPost(post) {
  const response = await fetch(`/posts/${post.id}`, {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(post)
  });

  if (!response.ok) {
    console.warn("Conflict detected! Resolving...");
    // Handle merge logic here
  }
}

This prevents data loss when multiple versions exist.