Cross View Cache Sync
Introduction: Why Cross-View Sync Matters
In the previous lesson, you learned how to filter tasks based on completion status.
Now imagine this scenario:
You open the Incomplete filter view and mark a task as Complete.
Then you switch to the Completed view — but the newly completed task doesn’t appear right away.
Or you go back to All Tasks, and the status hasn’t updated there either.
That inconsistency makes your app feel unreliable.
In this lesson, you’ll fix that by implementing Cross-View Cache Synchronization — ensuring that task updates propagate instantly across all views:
/tasks→ All Tasks/tasks/filter?completed=true→ Completed Tasks/tasks/filter?completed=false→ Incomplete Tasks/→ Dashboard summary counts
No page refreshes, no waiting for a full reload — everything stays perfectly in sync using SWR’s caching system.
The Goal: One Change, Everywhere
After completing this unit, your app will:
- Reflect task status changes instantly across All, Filtered, and Dashboard views.
- Automatically update counts (Total, Completed, Incomplete) on the Dashboard.
- Remove or add tasks in the appropriate filter views (e.g., completed tasks disappear from the “Incomplete” view immediately).
- Require no manual refresh — everything is handled through SWR’s
mutate()function.
Understanding the Problem
Each page in your app uses SWR to fetch and cache data independently:
/api/tasks→ used by/tasks(All Tasks)/api/tasks/filter?completed=true→ used by Completed filter view/api/tasks/filter?completed=false→ used by Incomplete filter view/api/tasks/:id→ used by the Task Detail Page
Each of these URLs is a unique cache key in SWR’s global store.
If you only update one cache key after toggling a task, the other caches remain stale — causing one view to show outdated data.
That’s where cross-view cache sync comes in.
You’ll explicitly tell SWR which caches to update using the mutate() function and a new prop: revalidateKeys.
How revalidateKeys Enables Cross-View Sync
Each TaskRow now accepts an optional prop named revalidateKeys.
It’s an array of cache keys that the component should refresh when a task’s status changes.
Example from the updated Tasks Page (src/app/(dashboard)/tasks/page.tsx):
What’s new here:
- The main Tasks Page does not need to pass
/api/tasksas arevalidateKeysvalue becauseTaskRowalready updates and revalidates that cache key directly. - Filtered views can still pass their filtered cache keys so
TaskRowknows which additional lists should update. - This same pattern will later extend to the filtered views, where you’ll pass multiple revalidate keys (e.g., both
/api/tasksand/api/tasks/filter?...).
Inside the TaskRow Component: Four Smaller Pieces
The full component is easiest to understand as four separate responsibilities:
- Main-list optimistic update — immediately update
/api/tasks. - Filtered cache update — move the task into or out of filtered lists.
- Server confirmation — send the
PATCHrequest. - Rollback and revalidation — recover if the server rejects the update.
Here is the complete version, followed by a breakdown of each piece:
Let’s break this down step by step:
1. Optimistic Updates
This is the familiar optimistic update pattern: update the visible /api/tasks cache first, then confirm with the server.
SWR updates the /api/tasks cache immediately.
The third parameter (false) means “don’t refetch yet” — we’re doing an optimistic update.
The result: the task’s completion status updates visually right away.
2. Updating All Filtered Lists
This ensures:
- Completed tasks disappear from
/tasks/filter?completed=false. - Incomplete tasks disappear from
/tasks/filter?completed=true.
Each affected cache is updated instantly.
3. Server Confirmation and Revalidation
Once the server responds:
- All caches are revalidated to ensure they match the server truth.
- If an error occurs, the caches are rolled back to the correct state.
- Toasts give clear success/error feedback.
Why This Works So Well
This approach leverages SWR’s global cache to maintain data consistency between multiple views that fetch the same resources differently.
Here’s what happens under the hood:
- You toggle a task’s status → SWR updates
/api/tasksinstantly. - Filtered lists (like
/api/tasks/filter?completed=false) update too. - Dashboard summary (which depends on
/api/tasks) automatically recalculates counts because it’s subscribed to the same cache. - When SWR revalidates, all caches confirm correctness with the server.
No reloading. No refetching entire pages. Just smooth, synchronized updates across every view.
Example in Action
- You’re on
/tasks/filter?completed=false. - You click “Mark Complete” on a task.
- The task instantly disappears from this list (it no longer matches the filter).
- If you switch to
/tasks/filter?completed=true, it’s already there. - On
/, the Dashboard’s “Completed” count increases immediately.
All of this happens without a single page reload.
Summary
In this lesson, you learned how to:
- Keep every task view in your app perfectly synchronized using SWR’s mutate().
- Use the
revalidateKeysprop to tell components which caches to refresh. - Ensure instant, cross-view updates between
/tasks,/tasks/filter, and/. - Automatically update dashboard summaries and filtered lists without reloading.
- Combine optimistic updates, error handling, and revalidation for production-level consistency.
Your task manager is now real-time at the UI level — every page stays accurate and responsive, no matter where changes happen.
In the next and final unit, you’ll wrap up by polishing your app’s mobile layout and production UI, giving it the look and feel of a professional web app.
