필사 모드: PWA & Offline-First Sync Engines 2026 Deep Dive - Workbox · Replicache · RxDB · Y.js · Automerge · LiveStore · ElectricSQL · Zero · Ground
English> "Local-first software is a system where ownership of the data lives on the user's device and the server merely assists with synchronization. By 2026 this is no longer an academic manifesto — it is the default architecture for actual products that Linear, Figma, Notion, and Tldraw deliver to millions of people every day." — Martin Kleppmann, 7-year anniversary keynote of the Ink & Switch local-first software manifesto (2025)
The evolution of the web app from "online-only forms" to "offline-first tools" passed through several stages: PWAs appearing in 2017, Workbox stabilizing in 2019, CRDTs going mainstream in 2021, Linear unveiling its Sync Engine in 2023, and finally the 2024-25 explosion of Replicache, Zero, ElectricSQL, LiveStore, Ground, and Triplit. As of May 2026 the central question is no longer "how do we cache with a Service Worker?" but rather **"which sync engine resolves the conflicts in our domain, and how do we bind a client-side SQLite or IndexedDB to a Postgres backend in both directions?"**
This article walks through the foundations of PWAs (Service Workers, the Cache API, IndexedDB, OPFS, Background Sync), the caching strategies in Workbox 8, the mutator model of Replicache and Zero, multi-tab sync in RxDB, the structure of Y.js and Automerge CRDTs, the Postgres-to-SQLite sync of ElectricSQL and PowerSync, the event-sourced approach of LiveStore, and finally case studies from Korean (Toss, Coupang Eats, Kakao) and Japanese (Mercari, Cookpad) PWA adopters.
1. Why offline-first matters again
Until the early 2020s the default web assumption was: "users are always online, the server is the source of truth, and the client is a transient view." Three forces from 2024-26 broke this assumption.
First, **user expectations have risen to native-app levels.** Linear creates an issue the instant you press a key, Figma keeps designing when the internet drops, and Notion lets you edit notes on a plane. All of these experiences share the same pattern: optimistic updates plus background synchronization.
Second, the **asymmetry of mobile networks**. In urban Korea and Japan 5G is seamless, but in Southeast Asia, Latin America, and India mobile markets, packet loss and 250 ms+ latency are still everyday reality. For global products, "always-online" becomes an increasingly unrealistic premise.
Third, **the tooling of CRDTs and sync engines**. In 2018 implementing a CRDT looked like it required a PhD. By 2026 you install Y.js, Automerge, Loro, Yrs, or the Tldraw store with a single package, and Replicache, Zero, ElectricSQL, and Triplit provide complete bidirectional sync from "one server endpoint plus a client SDK."
These three shifts together turned "offline-first" from a feature flag into **the new default architecture**.
2. The five pillars of a PWA: Service Worker, Manifest, Cache, IndexedDB, OPFS
A PWA (Progressive Web App) is not a single technology — it is a combination of five browser APIs.
| Component | Standard | 2026 support status |
|---|---|---|
| Service Worker | W3C/WHATWG | All modern browsers (including iOS 16.4+) |
| Web App Manifest | W3C | All browsers, iOS restricts some fields |
| Cache API | Service Workers spec | Broad support |
| IndexedDB | W3C | Broad support, Safari stabilized at 15 |
| Origin Private File System (OPFS) | WHATWG | Chrome 102+, Safari 17+, Firefox 111+ |
The Service Worker is a proxy thread sitting between the browser and the network. It intercepts fetch events to return cached responses or receives background-sync events to push queued mutations to the server. The Web App Manifest is a JSON file that declares "this site is installable," defining the home-screen icon, start URL, display mode (`standalone`, `minimal-ui`, `fullscreen`), and theme color.
The Cache API is a simple key-value store of Request/Response pairs, while IndexedDB is a transactional NoSQL object store. Finally OPFS, stabilized starting in 2023, is a relatively new API that gives each origin **its own isolated virtual file system**. With file handles and random-access I/O, SQLite WASM and DuckDB WASM run on top of OPFS as if they were operating on real files.
3. Service Worker lifecycle and pitfalls
The Service Worker looks simple but its lifecycle is unintuitive. You need to firmly grasp the order of `install` → `activate` → `fetch`/`message`/`sync`/`push` events, plus the concept of a "waiting worker" when a new build deploys.
The most common pitfalls are:
1. **Stale Service Worker** — even when a user refreshes the page, the active worker stays put and never caches the new build's assets. You must explicitly call `self.skipWaiting()` and `clients.claim()`.
2. **Cache key collisions** — Workbox's auto-generated precache manifests are hashed per build, but if you hand-write a cache name that matches across versions, the old cache cannot be replaced.
3. **Auth tokens leaking into the cache** — caching requests that include an `Authorization` header can expose another user's token. Cache strategies must be split by origin and headers.
4. **iOS's 7-day auto-clear** — Safari wipes IndexedDB, the Cache, and OPFS entirely if a user has not opened the PWA in seven days. "Permanent storage" is a lie.
4. The Workbox 8 caching strategy catalog
Workbox is Google's Service Worker helper library, and the 8.x line shipped in 2025 is the standard in 2026. The core idea is that you declaratively pick from five caching strategies.
- **CacheFirst** — if the cache has it, return it immediately; only hit the network if missing. Best for immutable assets like fonts, icons, and images.
- **NetworkFirst** — try the network first, fall back to the cache on failure. Best for API responses and HTML that change often.
- **StaleWhileRevalidate** — return the cache immediately while issuing a background network request to refresh it. The strategy with the best perceived UI speed.
- **NetworkOnly** — always go to the network, bypass the cache. For payment and authentication where freshness is mandatory.
- **CacheOnly** — rely entirely on the cache, offline-mode only.
Add to these `workbox-background-sync` queues, `workbox-broadcast-update` multi-tab notifications, `workbox-expiration` for TTLs, and `workbox-precaching` for shipped assets, and every pattern an ordinary PWA needs fits in fewer than 30 lines.
5. The reality of IndexedDB: Dexie and idb
IndexedDB is powerful but its raw API is callback- and event-based, which makes it hard to use. As of 2026 two libraries are de-facto standards.
**Dexie.js** (David Fahlander) — through `Dexie.Table`, `Dexie.Collection`, and `Dexie.liveQuery` it promises SQL-like queries, indexes, and transactions, plus a live-query layer compatible with RxJS. Multi-tab transaction sync stabilized in 9.x, and it is the default adapter for RxDB.
**idb** (Jake Archibald) — a small, thin Promise wrapper maintained at Google. Abstraction is minimal — it just wraps the IndexedDB API in promises. Workbox uses it internally.
The choice is clear: pick Dexie when your domain model is complex and you need live queries and indexes; pick idb for light tasks like queueing in a Service Worker or metadata storage.
6. OPFS combined with SQLite WASM
When the SQLite team officially shipped `sqlite3.wasm` with OPFS as a first-class backend in 2023, the door opened for real SQLite inside the browser. The implications are:
1. **Full SQL queries** — JOIN, GROUP BY, and window functions that were painful in IndexedDB now run on the client.
2. **Migrations** — `ALTER TABLE` and `PRAGMA user_version` give you the same migration patterns you use on the server.
3. **Portability** — ElectricSQL, PowerSync, and Cloudflare D1 all evolve as systems that share SQLite schemas.
As of 2026 the client builds **`@sqlite.org/sqlite-wasm`**, **`wa-sqlite`**, and **`@cloudflare/d1`** selectively support OPFS and IDB backends. ElectricSQL layers sync on top of wa-sqlite while PowerSync builds it on top of their own bundle.
7. A taxonomy of sync engines: seven axes
The tools labeled "sync engine" are not a single category. The 2026 market splits along at least seven axes.
| Axis | Options |
|---|---|
| Conflict resolution | LWW / server-authoritative / CRDT |
| Data model | Relational SQL / document / triple / event log |
| Client storage | IndexedDB / SQLite-OPFS / memory + files |
| Transport | WebSocket / SSE / long-poll / HTTP push |
| Server coupling | Postgres-direct / custom server / serverless |
| Indexing & query | Declarative query / SQL / Datalog / key lookup |
| Licensing | OSS / dual / SaaS-only |
Replicache and Zero are "server-authoritative + push/pull + mutator" models; ElectricSQL and PowerSync are "Postgres-to-local-SQLite" models; Y.js, Automerge, and Loro are "CRDT + arbitrary transport" models; LiveStore is an "event-sourcing + materialized view" model; Triplit and InstantDB are "triple/Datalog DB + live query" models.
8. The Replicache mutator model
Replicache (Rocicorp) is the tool that practically defined the sync-engine category in the early 2020s. Three concepts are at its core.
1. **Mutator** — a pure function implemented identically on the client and server. Defined as something like `createTodo(tx, args)`, it runs optimistically on the client, then re-runs on the server to produce the authoritative result.
2. **Push** — the client's mutation queue is sent to a server endpoint such as `/api/replicache/push`.
3. **Pull** — the server's diff is returned as a patch and applied to the client cache.
The server tracks "the latest mutation processed" using client ID and last-mutation-ID. Conflicts are resolved by the simple rule "the server is always right," and the client automatically rolls back and re-applies its optimistic state if the server patch disagrees.
This model is simpler than CRDTs and lets you write arbitrary domain logic. Its limitation is that it does not fit live multi-user collaboration on the same document — it shines in "each user edits their own data, occasionally sharing it" scenarios. That is why Linear has publicly said it drew inspiration from Replicache-style patterns early on.
9. Zero: the successor to Replicache
In 2024-25 Rocicorp announced **Zero**, which moves past Replicache's limits. The key differences are:
- **Declarative queries** — Replicache was KV-style; Zero offers SQL-like queries such as `z.query.issue.where(...).related(...)`.
- **Client-side live queries** — queries automatically translate into subscriptions so that React components re-render when underlying data changes.
- **Permission model** — row-level permissions are declared on the server side.
- **Server integration** — Postgres is assumed as the primary backend, and changes flow through Postgres logical replication.
As of May 2026 Zero is approaching GA and offers an incremental migration path for Replicache users. Linear maintains its own sync engine, but the Replicache → Zero pattern has become the default choice for teams starting from scratch.
10. RxDB 15.x: NoSQL offline-first
RxDB (Daniel Meyer) packages "reactive NoSQL DB + bidirectional sync" into a single library. Its core features are:
- **RxJS-based live queries** — collections and queries are Observables, so reactive UIs in React, Svelte, or Vue come for free.
- **Multiple storage adapters** — Dexie/IndexedDB, OPFS, in-memory, SQLite, plus the Premium adapters such as LokiJS, FoundationDB, and SharedWorker.
- **Bidirectional replication plugins** — bidirectional sync with CouchDB, GraphQL, REST, WebRTC, P2P, and more.
- **Multi-tab support** — multiple tabs on the same origin coordinate via BroadcastChannel and Web Locks, with a single tab elected leader.
RxDB does not push "Postgres-to-SQLite" — it pursues "bidirectional sync with arbitrary backends." Where ElectricSQL and PowerSync are SQL-centric, RxDB is document-centric, and both choices coexist meaningfully.
11. CRDT basics: from Last-Write-Wins to RGA, Yjs, and Automerge
A CRDT (Conflict-free Replicated Data Type) is a data structure that "automatically merges results from independently mutated replicas so that they converge to the same final state." The simplest form is the LWW Register (Last-Write-Wins), where the write with the larger timestamp wins. More elaborate forms include G-Counter, PN-Counter, OR-Set, and the RGA (Replicated Growable Array) variants used by Yjs and Automerge.
A comparison with OT (Operational Transformation) makes the contrast clear. OT requires hand-coded functions that "transform operation A to a moment after operation B when they happen concurrently," and Google Docs runs on this model. CRDTs take a different path: "make the writes themselves commutative and associative so that no transform functions are needed." CRDTs are P2P- and offline-friendly but have heavy metadata overhead; OT is efficient under a server-authoritative model.
12. Y.js: why Notion, Linear, and Tldraw chose it
Y.js (Kevin Jahns) is by 2026 the most widely adopted JavaScript CRDT library. Its core data structures are:
- `Y.Map` — a key-value map used for object collaboration
- `Y.Array` — an ordered array for list collaboration
- `Y.Text` — collaborative text at the character level
- `Y.XmlFragment` / `Y.XmlElement` — the collaboration backend for ProseMirror and TipTap
Y.js's strength is **compact binary updates**. Diffs are tiny enough to ship over arbitrary transports like WebSocket, WebRTC, or Hocuspocus. The **Awareness protocol** (cursors, selections, presence) is separated from the core, which makes it easy to adopt selectively.
Looking at adoption: Notion has moved parts of its collaborative text to Y.js, Linear uses Y.js for parts of comment and issue collaboration, and Tldraw v2 has its own store but ships an integration adapter for Y.js. Analyses suggest GitHub's new comment collaboration is also Y.js-based.
13. Automerge 2 and Automerge-Repo
Automerge (Martin Kleppmann and Ink & Switch) is, alongside Y.js, the most prominent CRDT library. While 1.x was written in JavaScript, 2.x was rewritten as a **Rust core with WASM bindings**, delivering a major performance jump.
The core features are:
- **JSON-shaped CRDT** — models data in a form that is essentially a 1:1 match with JavaScript objects.
- **Time travel** — every change is preserved, so reverting to any prior state is possible.
- **Automerge-Repo** — a higher-level layer that standardizes document repositories, sync, and network adapters.
Automerge has slightly more metadata than Y.js, but the "data model maps 1:1 to JSON" intuition is its strong suit. Adoption is high in small collaborative tools (notes, todos, diagrams).
14. Loro: the next-generation CRDT written in Rust
Loro is a new CRDT library that appeared in 2024, written in Rust and shipped with WASM bindings. Its strengths are:
- **Metadata compression** — updates are 30-50% smaller than Y.js or Automerge.
- **Rich data types** — Text, List, Map, Tree, and Counter are all supported.
- **Time travel + branch/merge** — a branching model reminiscent of Git.
As of May 2026 Loro has entered stabilization, and some new projects have started picking it over Y.js. Catching up to Y.js's ecosystem in the short term is hard, but Loro firmly occupies the "next-generation candidate" slot.
15. ElectricSQL: bridging Postgres and local SQLite
ElectricSQL is a sync engine that emerged in 2023-24, and it can be summarized in one line: **"replicate a schema defined in Postgres into a client-side SQLite and synchronize it bidirectionally."**
The operation looks like this:
1. Mark certain Postgres tables as sync targets with the `electrify` SQL function.
2. The ElectricSQL server subscribes to Postgres logical replication and pulls in the diffs.
3. The client (Web, React Native, or Tauri) maintains a local replica using wa-sqlite or sqlite-wasm.
4. The client reads and writes with normal SQL; writes accumulate in a queue and propagate to Postgres through the ElectricSQL server.
Of particular note is conflict resolution. The default policy is "per-column LWW + Postgres constraints," but enabling Rich-CRDT mode lets you directly map CRDT data types such as counters and OR-Sets.
16. PowerSync: synchronizing mobile and SQLite
PowerSync is an Australian startup that grew rapidly after its 2024 Series A. Compared to ElectricSQL, the differences are:
- **Mobile-first** — supports React Native, Flutter, Capacitor, and Web, but the mobile SDKs are the most mature.
- **Multiple backends** — beyond Postgres, MySQL and MongoDB are supported.
- **Custom conflict resolution** — the model where the server re-runs mutations defined as JS functions, similar to Replicache.
- **Dual licensing** — commercial managed plus self-hosted options.
Where ElectricSQL focuses on OSS and Postgres, PowerSync aims at mobile enterprise. Both share the trait that "client-side SQLite" is the core.
17. LiveStore: event-sourced local-first
LiveStore (Schickling Cap, funded by Shopify and Garden) is one of the most-watched new sync engines of 2024-25. Its approach is distinctive.
- **Event sourcing is primary** — every mutation is logged as an event, and the client materializes views by replaying the event stream.
- **Reflect-style API** — subscribing to a query from a React Hook means the value is automatically recomputed whenever the event stream changes.
- **Local-first plus server sync** — the server is a simple event-log store, and conflict resolution flows from event order.
- **Independent of GraphQL/REST** — its own SDK manages the sync channel.
In the same category sit **Triplit** (a triple-based DB with live queries) and **InstantDB** (a Datalog-based DB with a Firebase-style SDK). All three share the goal of "delivering a BaaS-class developer experience on top of a local-first runtime."
18. WatermelonDB, PouchDB, TanStack DB, Convex
Both old and new tools coexist in the market.
- **WatermelonDB** — a React-Native-centric local DB whose backend sync protocol you implement yourself. Adopted by some RN apps including parts of Mercari.
- **PouchDB + CouchDB** — the classic combination popular in the late 2010s. Stable but based on LWW + revision trees rather than CRDTs.
- **Firestore offline / Firebase Realtime DB** — the widest BaaS option, with offline queues and optimistic writes built in.
- **Convex** — a reactive backend that emerged in 2022. The backend auto-invalidates whenever client queries are subscribed. Not truly local-first, but it provides a "realtime plus optimistic" experience cheaply.
- **TanStack DB** — Tanner Linsley's next-generation client DB from the TanStack Query author. Alpha in 2025, beta in 2026.
The deciding factor is "what stack the team already runs." Firebase users gravitate to Firestore offline, React Native plus a custom server tends toward WatermelonDB or PowerSync, and a GraphQL backend dovetails with Convex.
19. Real-time transports: WebSocket, SSE, HTTP push
How a sync engine ships diffs directly determines client performance and reliability. The standard 2026 options are three.
- **WebSocket** — bidirectional, low-latency. Used by Y.js's `y-websocket`, some Replicache adapters, and ElectricSQL's transport. The downside is proxy and CDN compatibility.
- **SSE (Server-Sent Events)** — one-way server-to-client, HTTP/2-compatible. Used in parts by Zero, Convex, and others. The downside is that being one-way means client pushes need a separate HTTP request.
- **Long-poll / HTTP push** — Replicache's default. The most conservative choice, with excellent CDN and proxy compatibility but high latency.
WebTransport (over HTTP/3) and the WebRTC DataChannel are also used in some P2P scenarios, but in 2026 WebSocket and SSE remain the mainstream.
20. The schema migration problem
The hardest problem in any offline-first system is **how to handle offline clients when the schema changes**. Imagine a user does not open the app for a month and on returning finds that the server's schema has changed twice — how should their unflushed mutations be processed?
Patterns seen in production are:
1. **Version gates** — if the client build is too old, show "update required" and block sync.
2. **Bidirectionally compatible schemas** — only add new columns, and defer deletions for N versions.
3. **Mutation transformation** — the server automatically converts old-form mutations to the new form (a sync-engine analogue of database migration).
4. **Event-sourcing advantage** — when events are primary as in LiveStore, you only need event-schema compatibility, and views can be freely re-derived.
This problem has no single right answer — each team must compromise based on its domain.
21. The Linear sync engine architecture
Linear has shared its sync engine architecture through a 2024 conference talk and an extensive blog series. The core elements are:
- **All data mirrored into the client cache** — the entire dataset is downloaded into IndexedDB during the first 30 seconds after login.
- **Object-graph sync** — diffs are transmitted at the granularity of objects such as Issue, Project, and Cycle.
- **Delta-based** — only the fields that changed are pushed, not the whole object.
- **Permission filtering** — the server sends only the objects each user is allowed to see.
- **WebSocket + HTTP push** — real-time changes come over WebSocket, initial sync over HTTP.
The Linear mantra is "the UI reflects you the instant you press a key — perceived 0 ms," and that is possible because "the data is already on the client and every mutation runs optimistically." The same pattern appears in variations across Notion, Figma, and Tldraw.
22. The Figma multiplayer model
Figma has run a custom CRDT-like multiplayer model since 2016. The famous 2020 engineering post "How Figma's multiplayer technology works" lays out the essentials.
- **Partial LWW + object trees** — shapes form a tree, and each property is merged with LWW.
- **Server-authoritative + client cache** — the server decides authoritative ordering but the client runs optimistically.
- **Camera and cursor are ephemeral** — synchronized but not persistently stored.
- **Custom binary protocol** — efficient serialization instead of JSON.
By choosing a domain-specific model rather than a general-purpose CRDT, Figma gained large memory and bandwidth wins. But this approach is only viable when the domain is narrow enough and stable enough to permit it.
23. Korean PWA adoption: Toss, Coupang Eats, Kakao
PWA adoption in the Korean market rests on the high share of mobile web traffic.
- **Toss** — applied PWA patterns to some payment and money-transfer pages to deliver stable experiences even to users without the mobile app installed. Service-Worker-based caches aggressively optimize LCP and INP.
- **Coupang Eats** — pursues an experience nearly equivalent to the native app on mobile web, using Workbox-based cache strategies. Offline-queue use is steadily growing in marketplace scenarios.
- **Kakao** — applies PWA manifests and Service Workers to some Kakao Work pages. Absorbs B2B SaaS demand for "installable web apps."
The Android skew among Korean users is friendly to PWAs, and iOS's 7-day clear constraint has relatively muted impact.
24. Japanese PWAs: Mercari and Cookpad
The Japanese market has a balance of mobile web and native apps similar to Korea.
- **Mercari** — applies PWA to a lightweight mobile-web variant to absorb users in some emerging markets. Not a CRDT or sync engine, but Workbox-based caching is in active use.
- **Cookpad** — uses Service Workers to cache recipe pages on mobile web, enabling offline access to favorited recipes.
- **Rakuten** — added a PWA manifest to some product pages.
The Japanese particularity is "strong native-app-first culture but mobile web traffic still non-trivial." PWAs have positioned themselves as "a backup for users who haven't installed the app."
25. Use-case catalog: notes, tasks, drawing, code editors
Offline-first patterns shine in clear domains.
- **Notes apps** — Reflect, Obsidian Sync, Logseq, Mem. Reflect runs its own sync engine, Obsidian Sync centers on E2EE, Logseq is graph-DB-based.
- **Task apps** — Linear, Things, TickTick. Linear is the textbook example of a sync engine.
- **Drawing & whiteboard** — Tldraw, Excalidraw, Figma, Miro. Tldraw v2 has its own store with a Y.js adapter.
- **Code editors** — Replit Multiplayer, Stackblitz, CodeSandbox. Replit uses a custom CRDT; the others lean on Y.js or OT.
- **Document collaboration** — Notion, Coda, Quip. Notion blends OT and CRDT.
A common pattern: structured data such as tables and issues prefers "server-authoritative + mutator," while unstructured text and drawings prefer CRDTs.
26. The 2026 decision guide: which sync engine should you pick
Compressing the previous 25 sections into a decision tree:
1. **Do you just need basic PWA caching?** → Workbox 8 + IndexedDB (Dexie) is enough.
2. **Each user edits their own data?** → Replicache or Zero.
3. **Is Postgres already the source of truth?** → ElectricSQL (OSS-first) or PowerSync (mobile-first).
4. **Multiple users editing one document concurrently?** → Y.js (most battle-tested), Automerge (JSON-friendly), or Loro (next-gen).
5. **Developer-experience-first, BaaS-like SDK?** → Convex (reactive) or Triplit / InstantDB / LiveStore (local-first).
6. **React Native mobile + your own backend?** → WatermelonDB or PowerSync.
Finally, whatever tool you pick, **migration, permissions, E2EE, and observability** need separate design. The sync engine solves the synchronization problem — it does not solve the whole of data governance.
27. References
- Local-first software, Ink & Switch: https://www.inkandswitch.com/local-first/
- Talks by Martin Kleppmann: https://martin.kleppmann.com/
- Web App Manifest spec: https://www.w3.org/TR/appmanifest/
- Service Worker spec: https://www.w3.org/TR/service-workers/
- Workbox official documentation: https://developer.chrome.com/docs/workbox
- Cache API: https://developer.mozilla.org/en-US/docs/Web/API/Cache
- IndexedDB API: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
- Origin Private File System: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system
- SQLite WASM official site: https://sqlite.org/wasm/doc/trunk/index.md
- Replicache official site: https://replicache.dev
- Zero (Rocicorp) official site: https://zero.rocicorp.dev
- RxDB official site: https://rxdb.info
- Dexie.js official site: https://dexie.org
- Y.js official site: https://yjs.dev
- Automerge official site: https://automerge.org
- Loro official site: https://loro.dev
- ElectricSQL official site: https://electric-sql.com
- PowerSync official site: https://www.powersync.com
- LiveStore official site: https://livestore.dev
- Triplit official site: https://www.triplit.dev
- InstantDB official site: https://www.instantdb.com
- WatermelonDB on GitHub: https://github.com/Nozbe/WatermelonDB
- Convex official site: https://www.convex.dev
- TanStack DB official site: https://tanstack.com/db
- Linear Sync Engine blog: https://linear.app/blog/scaling-the-linear-sync-engine
- Figma multiplayer engineering blog: https://www.figma.com/blog/how-figmas-multiplayer-technology-works/
- Tldraw official site: https://tldraw.dev
현재 단락 (1/191)
The evolution of the web app from "online-only forms" to "offline-first tools" passed through severa...