Documentation Index
Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
Use this file to discover all available pages before exploring further.
The sync package provides a client-side runtime for building offline-first applications. It combines reactive state primitives (Signals, Computed values, Effects) with synchronized collections that stay in sync with a server.
What is Offline-First?
In a traditional web application, the server is the source of truth. When youβre offline, the app doesnβt work.
Traditional App:
βββββββββββ Request βββββββββββ
β Client β ββββββββββββ> β Server β
β β <ββββββββββββ β β
βββββββββββ Response βββββββββββ
If server is unreachable = App broken
In an offline-first application, the client keeps a local copy of the data. Changes are queued locally and synced when connectivity is available.
Offline-First App:
βββββββββββββββββββββββββββββββββββββββ
β Client β
β βββββββββββββββ ββββββββββββββββ β
β β Local State β β Mutation β β
β β β β Queue β β
β βββββββββββββββ ββββββββββββββββ β
β β β β
β β β β
βββββββββββββββββββββββββββββββββββββββ
β Background β
βΌ Sync βΌ
βββββββββββ
β Server β
βββββββββββ
If server is unreachable = App still works!
Benefits:
- App works without internet
- Instant UI updates (no loading spinners)
- Better user experience on slow connections
- Resilience to network failures
What the Sync Package Provides
Reactive Primitives
| Type | Description |
|---|
Signal[T] | A reactive value container |
Computed[T] | A derived value that updates automatically |
Effect | A side effect that runs when dependencies change |
These provide fine-grained reactivity similar to SolidJS or Vueβs Composition API.
Synchronized State
| Type | Description |
|---|
Client | Manages server sync, mutation queue, and connection state |
Collection[T] | A reactive set of entities that syncs with the server |
Entity[T] | A single synchronized record |
The Sync Protocol
The client and server communicate using a simple protocol:
ββββββββββββββ ββββββββββββββ
β Client β β Server β
ββββββββββββββ ββββββββββββββ
β β
βββββ Push(mutations) βββββββββββββ>β
β β
β<βββ Pull(cursor) ββββββββββββββββββ
β Returns changes since cursor β
β β
β<βββ Snapshot(scope) βββββββββββββββ
β Returns full state β
β β
- Push: Client sends queued mutations to server
- Pull: Client requests changes since last sync point (cursor)
- Snapshot: Client requests full state (initial load or resync)
Reactive Primitives
A Signal holds a value and notifies subscribers when it changes:
import "github.com/go-mizu/mizu/view/sync"
// Create a signal with initial value
count := sync.NewSignal(0)
// Read the value
fmt.Println(count.Get()) // 0
// Update the value
count.Set(5)
fmt.Println(count.Get()) // 5
// Update with a function
count.Update(func(v int) int {
return v + 1
})
fmt.Println(count.Get()) // 6
Computed
A Computed value derives from other signals and updates automatically:
count := sync.NewSignal(10)
price := sync.NewSignal(5.0)
// Automatically tracks dependencies
total := sync.NewComputed(func() float64 {
return float64(count.Get()) * price.Get()
})
fmt.Println(total.Get()) // 50.0
count.Set(20)
fmt.Println(total.Get()) // 100.0 (auto-updated!)
An Effect runs a function whenever its dependencies change:
count := sync.NewSignal(0)
effect := sync.NewEffect(func() {
fmt.Printf("Count is now: %d\n", count.Get())
})
// Prints: "Count is now: 0"
count.Set(1)
// Prints: "Count is now: 1"
count.Set(2)
// Prints: "Count is now: 2"
// Stop the effect
effect.Stop()
Collections and Entities
Collections manage groups of entities that sync with a server:
// Define your data type
type Todo struct {
Title string `json:"title"`
Completed bool `json:"completed"`
}
// Create a client
client := sync.New(sync.Options{
BaseURL: "http://localhost:8080/_sync",
Scope: "user:123",
})
// Create a collection
todos := sync.NewCollection[Todo](client, "todo")
// Create an entity
todo := todos.Create("todo-1", Todo{
Title: "Buy groceries",
Completed: false,
})
// Read (reactive - tracks dependencies)
fmt.Println(todo.Get().Title)
// Update
todo.Set(Todo{
Title: "Buy groceries",
Completed: true,
})
// Delete
todo.Delete()
// Query the collection
all := todos.All() // All entities (reactive)
count := todos.Count() // Entity count (reactive)
found := todos.Find(func(t Todo) bool {
return !t.Completed
})
Quick Example
Hereβs a complete example showing the sync system:
package main
import (
"context"
"fmt"
"time"
"github.com/go-mizu/mizu/view/sync"
)
type Todo struct {
Title string `json:"title"`
Completed bool `json:"completed"`
}
func main() {
// Create client
client := sync.New(sync.Options{
BaseURL: "http://localhost:8080/_sync",
Scope: "user:demo",
OnSync: func(cursor uint64) {
fmt.Printf("Synced to cursor %d\n", cursor)
},
OnOnline: func() {
fmt.Println("Connected!")
},
OnOffline: func() {
fmt.Println("Offline - changes will sync later")
},
})
// Start sync
ctx := context.Background()
client.Start(ctx)
defer client.Stop()
// Create a collection
todos := sync.NewCollection[Todo](client, "todo")
// Create a todo (works offline!)
todo := todos.Create("todo-1", Todo{
Title: "Learn sync",
})
// Create an effect to react to changes
sync.NewEffect(func() {
t := todo.Get()
status := "pending"
if t.Completed {
status = "done"
}
fmt.Printf("Todo: %s [%s]\n", t.Title, status)
})
// Update the todo
todo.Set(Todo{
Title: "Learn sync",
Completed: true,
})
// Wait a bit to see sync happen
time.Sleep(2 * time.Second)
}
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client β
β β
β βββββββββββββββββ ββββββββββββββββββββββββββββ β
β β Reactive β β Sync Engine β β
β β Layer β β β β
β β β β ββββββββββββββββββββββ β β
β β Signal[T] β β β Mutation Queue β β β
β β Computed[T] β β β (offline-safe) β β β
β β Effect β β ββββββββββββββββββββββ β β
β β β β β β
β βββββββββββββββββ β ββββββββββββββββββββββ β β
β β β β Store β β β
β βΌ β β (local state) β β β
β βββββββββββββββββ β ββββββββββββββββββββββ β β
β β Collections β β β β
β β ββββββ€ ββββββββββββββββββββββ β β
β β Entity[T] β β β Cursor β β β
β β β β β (sync position) β β β
β βββββββββββββββββ β ββββββββββββββββββββββ β β
β β β β
β ββββββββββββββββββββββββββββ β
β β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββ΄βββββββββββββββ
β β
βΌ βΌ
Push/Pull Live Updates
(HTTP) (WebSocket)
β β
ββββββββββββββββ¬βββββββββββββββ
β
βΌ
βββββββββββββββ
β Server β
βββββββββββββββ
When to Use Sync
Use sync when:
- Building offline-capable applications
- You need instant UI updates without loading states
- Multiple clients need to see synchronized data
- You want fine-grained reactivity in Go
Consider alternatives when:
- Building server-side only applications
- You donβt need offline support
- Simple request/response patterns are sufficient
Whatβs Next?
- Quick Start - Build your first synced app
- Client - Client configuration and lifecycle
- Reactive - Deep dive into Signal, Computed, Effect
- Collections - Working with synchronized data
- Integration - Combining sync with live for real-time updates