Skip to main content
Projects
Independent Engineering Analysis~9 min read

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

01

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.

02

Single boolean state

A page can be offline for multiple reasons: explicit download, recent activity, or inherited availability from a parent page.

03

Changing hierarchy

Pages move between parents and databases. A child can lose one inherited offline reason while still needing to preserve another independent reason.

04

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.

01

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.

02

Provenance model

Notion separated the derived result from the cause: offline_page represents availability, while offline_action stores why that availability exists.

03

Offline forest reconciliation

Hierarchy changes are reconciled as insert/delete operations so stale inherited reasons are removed without deleting valid independent intent.

04

Freshness and reconnect loop

Push updates while connected and catch-up after reconnection keep local storage aligned with server changes.

Notion's approach

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.

Notion's approach

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.

Notion's approach

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 plan

No reconciliation operations yet.

Hierarchical derived state requires reconciliation, not blind replacement.

Notion's approach

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.

Engineering principles

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.