Shared mode lets multiple LiveView clients work against the same accepted canvas state.
The coordination machinery changes. The command model does not. Adding an item, moving a graph node, committing a vector item, restoring layout, or removing a selection still goes through the same canvas command path.
Use this guide to add shared mode to an app. Use Operate Shared Canvases for
production backend and recovery details.
Start With Local Mode
Local mode is the default:
use LivePanels.Canvas,
pubsub: MyApp.PubSubEach mounted canvas LiveView owns its own state unless your app deliberately shares a canvas id and backend.
Start with local mode while building the item model and UI. Move to shared mode when users should collaborate in the same room, board, workflow, or document.
Enable Shared Mode
Shared mode needs:
- the LivePanels application child in your supervision tree;
- a PubSub module;
session: [mode: :shared];- a stable
sessionid; - a shared backend.
For a one-node app:
children = [
{Phoenix.PubSub, name: MyApp.PubSub},
LivePanels.Application
]defmodule MyAppWeb.RoomLive do
use MyAppWeb, :live_view
use LivePanels.Canvas,
pubsub: MyApp.PubSub,
session: [
mode: :shared,
id: {:param, "room_id"},
backend: LivePanels.Canvas.Shared.Local
]
endNow clients that visit the same route parameter share the same canvas state.
Choose A Canvas Id
The canvas id is the coordination key.
Good canvas ids are stable and product-owned:
session: [id: {:param, "board_id"}]or:
session: [id: "support-war-room"]Avoid per-tab ids when you want collaboration. Avoid ids derived from display state that can change during a session.
If the canvas id identifies a product record, authorize access to that record in your Phoenix route, mount logic, or canvas policy. Shared mode coordinates canvas state; it does not replace app-level authorization.
Participants
Each connected browser has a participant identity and per-participant runtime projection.
Use read helpers when the application needs participant information:
participants = LivePanels.Canvas.Read.participants(socket)
client_id = LivePanels.client_id(socket)Selection, history, clipboard, viewport, and focus can be actor-scoped even while the canvas state is shared. That lets two users inspect different parts of the same room without fighting over every local UI detail.
What Gets Shared
Shared mode coordinates accepted canvas state:
- item add, remove, placement, visibility, props, and z-order;
- graph connectors and graph selection commands;
- committed vector items and vector item edits;
- layout restore;
- persistent shared state snapshots or deltas;
- participant presence metadata.
It does not automatically make all application UI state shared. Your toolbar mode, open inspector, filter controls, unsaved form fields, and route-level app state remain application concerns unless you explicitly model them as canvas state or app props.
Persistence
Configure a durable layout store when shared canvas state should recover after process or server restart:
defmodule MyApp.CanvasLayouts do
use LivePanels.Canvas.LayoutStore.Ecto,
repo: MyApp.Repo,
schema: MyApp.LivePanelsCanvasLayout
end
use LivePanels.Canvas,
pubsub: MyApp.PubSub,
session: [
mode: :shared,
id: {:param, "room_id"},
backend:
{LivePanels.Canvas.Shared.Local,
layout_store: MyApp.CanvasLayouts}
]For local development, the default ETS store is useful. For production shared canvases, persist to a database-backed store and run the shared backend health check against that store.
Subscribe To Runtime Streams
Application code can subscribe to canvas runtime events:
def mount(_params, _session, socket) do
if connected?(socket), do: LivePanels.subscribe(socket, :events)
{:ok, socket}
endUse subscriptions for app integration, analytics, status display, or external process coordination. Use commands for mutations.
Graph And Drawing In Shared Mode
Graph and drawing do not need separate collaboration models.
Graph connectors and drawing primitives are canvas items after commit. Because of that, shared mode can coordinate them through the same accepted command path used by normal item movement.
In-progress drawing drafts are still not canonical state, but shared canvases
broadcast throttled draw intent so other participants can see live previews.
The final vector item is shared through the accepted add_vector_item
command.
What LivePanels Owns
LivePanels owns shared coordinator startup, command routing, accepted state fanout, participant tracking, recovery hooks, and projection to each client.
Your app owns room routing, user authorization, durable layout storage, deployment topology, and product-specific collaboration behavior.