Caching a page is not the same as making it available offline.
A Notion-focused architecture study on how a best-effort local cache becomes a persistent offline system with dependency guarantees, provenance, hierarchy reconciliation, and freshness.
Source: Notion Engineering, "How we made Notion available offline". Independent analysis, not affiliated with Notion.
Product requirement
- Fully render the page offline
- Preserve required dependencies
- Keep offline content current
- Explain why the page is offline
- Reconcile correctly after hierarchy changes
Problem
Solution
Best-effort cache
Notion already had a local SQLite cache, but cached data could be evicted, incomplete, or missing the records needed to render a page.
Single boolean state
A page can be offline for multiple reasons: explicit download, recent activity, or inherited availability from a parent page.
Changing hierarchy
Pages move between parents and databases. A child can lose one inherited offline reason while still needing to preserve another independent reason.
Stale offline content
Downloading a page once does not keep it trustworthy. Users can go offline, reconnect later, and expect the page to reflect newer server state.
Explicit offline guarantee
Offline mode had to define the full page dependency graph and persist the required records deliberately, not hope the cache still had them.
Provenance model
Notion separated the derived result from the cause: offline_page represents availability, while offline_action stores why that availability exists.
Offline forest reconciliation
Hierarchy changes are reconciled as insert/delete operations so stale inherited reasons are removed without deleting valid independent intent.
Freshness and reconnect loop
Push updates while connected and catch-up after reconnection keep local storage aligned with server changes.
Cache vs guarantee
Notion already had a SQLite cache, but the article makes the important distinction: a cache can contain records without guaranteeing which records are present, how long they remain, or whether every dependency required to render a page exists locally. A page is a graph: blocks, images, comments, mentions, database records, metadata, and permission-sensitive state.
Dependency graph demo
Page status: partially renders
Caching describes what happens to be local. Offline availability defines what must be local.
offline_page / offline_action
A set like offline_pages = { Page A, Page B } breaks when one page has multiple reasons to be offline. If Page X is both explicitly downloaded and retained because of recent activity, removing the explicit toggle should not remove the page from offline availability.
offline_page
Page X: offline
offline_action
Page X -> Explicit download
Page X -> Recent activity
Store provenance when you need to safely derive and reverse state.
Offline forest
Inherited availability turns offline pages into a forest. Pages move between parents, database entries appear and disappear, and descendants can be held offline by inherited and independent reasons at the same time. Reconciliation must add missing inherited reasons, remove stale ones, and preserve independent intent.
Workspace
-> Product database
-> Roadmap
-> Architecture review (independent: favorited)
-> Launch planNo reconciliation operations yet.
Hierarchical derived state requires reconciliation, not blind replacement.
Freshness after download
Downloading once is not enough. A trustworthy offline system has to stay current while connected and catch up after reconnection. Otherwise the product gives users stale local data while still implying confidence.
While connected
Server change -> notification -> affected data fetched -> local storage updated.
After reconnection
Client reconnects -> compares known state or timestamps -> downloads newer records -> restores consistency.
Persistence makes data available. Synchronization keeps it trustworthy.
What Notion teaches
Define the complete offline unit.
Notion example
A Notion page requires every record needed to render it.
Ask your team
What exact data must exist locally before we can truthfully call this item available offline?
Treat local persistence as product infrastructure.
Notion example
The SQLite cache became a durable, first-class store, not only a request-speed optimization.
Ask your team
Is our local storage designed to be relied on, or only to speed up the next request?
Store why derived state exists.
Notion example
Notion separates offline_page from offline_action so one result can have many causes.
Ask your team
Where else do we collapse multiple causes into one boolean?
Preserve independent causes during reconciliation.
Notion example
Removing one inherited reason must not remove a page held offline by another reason.
Ask your team
Does our reconciliation logic replace state, or reconcile it?
Design freshness and reconnection from the start.
Notion example
Push updates plus reconnection catch-up keep offline content current.
Ask your team
What happens to our cached data after an hour offline? A day?
What I learned
- Product guarantees shape data models.
- Local storage is only the beginning.
- Provenance makes derived state reversible.
- Reconciliation must preserve valid intent.
- Offline-first is a product and backend commitment, not just a frontend feature.
Independent architectural analysis. Not affiliated with, endorsed by, or representing Notion Labs, Inc.
