# Getting Started with Grove
Grove helps you build apps where data syncs automatically—even when users edit the same thing at the same time, even when they're offline.
No conflict resolution dialogs. No "your changes were overwritten." It just works.
## Installation
Add `grove` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:grove, "~> 0.1.0"}
]
end
```
Then run:
```bash
mix deps.get
```
## Your First Tree (2 minutes)
Grove stores data in trees—perfect for forms, documents, outlines, or any hierarchical data.
```elixir
# Start an interactive session
iex -S mix
# Create a tree with a unique replica ID
tree = Grove.Tree.new("my_laptop")
# Add some nodes
node1 = %Grove.Node{id: "task_1", type: :task, attrs: %{title: "Buy groceries"}}
node2 = %Grove.Node{id: "task_2", type: :task, attrs: %{title: "Walk the dog"}}
tree = tree
|> Grove.Tree.put_node(node1)
|> Grove.Tree.put_node(node2)
# Get a node
Grove.Tree.get_node(tree, "task_1")
# => %Grove.Node{id: "task_1", type: :task, attrs: %{title: "Buy groceries"}, ...}
# Update a node
tree = Grove.Tree.update_node(tree, "task_1", fn node ->
%{node | attrs: Map.put(node.attrs, :done, true)}
end)
```
That's it! You have a working tree.
## The Magic: Automatic Merge (3 minutes)
Here's where Grove shines. Let's simulate two devices editing the same data:
```mermaid
sequenceDiagram
participant A as Device A (Laptop)
participant B as Device B (Phone)
Note over A,B: Both start with same data
A->>A: Add "task_from_laptop"
B->>B: Add "task_from_phone"
Note over A,B: Devices are offline/concurrent
A->>B: Sync operations
B->>A: Sync operations
Note over A,B: Both now have ALL changes!
```
```elixir
# Device A: Create a tree
tree_a = Grove.Tree.new("laptop")
tree_a = Grove.Tree.put_node(tree_a, %Grove.Node{
id: "note_1",
type: :note,
attrs: %{text: "Original note"}
})
# Device B: Gets the same starting point
tree_b = Grove.Tree.new("phone")
tree_b = Grove.Tree.put_node(tree_b, %Grove.Node{
id: "note_1",
type: :note,
attrs: %{text: "Original note"}
})
# Now they BOTH edit at the same time (simulating offline/concurrent edits)
# Device A adds a task
tree_a = Grove.Tree.put_node(tree_a, %Grove.Node{
id: "task_from_laptop",
type: :task,
attrs: %{title: "Added on laptop"}
})
# Device B adds a different task
tree_b = Grove.Tree.put_node(tree_b, %Grove.Node{
id: "task_from_phone",
type: :task,
attrs: %{title: "Added on phone"}
})
# Get the pending operations from each device
{tree_a, ops_a} = Grove.Tree.flush_pending_ops(tree_a)
{tree_b, ops_b} = Grove.Tree.flush_pending_ops(tree_b)
```
Now the magic—sync them:
```elixir
# Device A receives Device B's changes
tree_a = Enum.reduce(ops_b, tree_a, fn {_id, event}, acc ->
case Grove.Tree.apply_remote(acc, event) do
{:ok, updated} -> updated
{:duplicate, existing} -> existing
end
end)
# Device B receives Device A's changes
tree_b = Enum.reduce(ops_a, tree_b, fn {_id, event}, acc ->
case Grove.Tree.apply_remote(acc, event) do
{:ok, updated} -> updated
{:duplicate, existing} -> existing
end
end)
# Check: Do they have the same nodes?
Map.keys(tree_a.nodes) |> Enum.sort()
# => ["note_1", "task_from_laptop", "task_from_phone"]
Map.keys(tree_b.nodes) |> Enum.sort()
# => ["note_1", "task_from_laptop", "task_from_phone"]
# Both devices now have ALL the changes!
```
**No conflicts. No data loss. They just merged.**
## What Just Happened?
Grove uses a technology called **CRDTs** (Conflict-free Replicated Data Types). The short version:
```mermaid
flowchart LR
subgraph "Traditional Sync"
T1[User A edits] --> TC{Conflict!}
T2[User B edits] --> TC
TC --> TL[Data loss or manual merge]
end
subgraph "Grove (CRDT)"
G1[User A edits] --> GM[Automatic Merge]
G2[User B edits] --> GM
GM --> GR[Both changes preserved]
end
```
**How it works:**
1. **Every change is an operation** — "add node X", "update node Y"
2. **Operations have unique IDs** — Based on replica + timestamp
3. **Merge is mathematical** — Same inputs always produce same output
4. **Order doesn't matter** — A+B = B+A (commutative)
This means:
- Users can edit offline, sync later
- Multiple users can edit simultaneously
- No central server needed for conflict resolution
- Data never gets lost or overwritten unexpectedly
## Real-World Integration
In a real app, you'd use Phoenix PubSub or similar to broadcast operations:
```elixir
# When local changes happen
{tree, ops} = Grove.Tree.flush_pending_ops(tree)
Phoenix.PubSub.broadcast(MyApp.PubSub, "doc:#{doc_id}", {:grove_ops, ops})
# When remote changes arrive
def handle_info({:grove_ops, ops}, socket) do
tree = apply_remote_ops(socket.assigns.tree, ops)
{:noreply, assign(socket, tree: tree)}
end
```
Grove also provides `Grove.Session` for managing document sessions with multiple subscribers.
## Next Steps
- **[Concepts](concepts.md)** — Understand how CRDTs work and why Grove is fast
- **[Use Cases](use-cases.md)** — See real-world applications and patterns
- **[Architecture](architecture.md)** — Deep dive into Grove's internals
- **[Benchmarks](benchmarks.md)** — Performance characteristics
## Quick Reference
| Operation | Code |
|-----------|------|
| Create tree | `Grove.Tree.new("replica_id")` |
| Add node | `Grove.Tree.put_node(tree, node)` |
| Get node | `Grove.Tree.get_node(tree, id)` |
| Update node | `Grove.Tree.update_node(tree, id, fn)` |
| Delete node | `Grove.Tree.delete_node(tree, id)` |
| Move node | `Grove.Tree.move_node(tree, id, new_parent_id)` |
| Get pending ops | `Grove.Tree.flush_pending_ops(tree)` |
| Apply remote op | `Grove.Tree.apply_remote(tree, event)` |