Examples menu

Composition API

Save a name for all your tabs, click a counter that adds one more each time, and vote for an animal with every other visitor. The page and its vote widgets are classes built with #[Signal], #[Signal(Scope::SESSION)], #[Signal(Scope::GLOBAL)], #[Persist] and #[Action].

#[Signal] creates a TAB-scoped reactive signal backed by a client-visible store entry. Client-writable via data-bind. Each browser tab has its own isolated copy.
#[Persist] is a plain server-side instance property, not a signal, not visible to the client. Because the class instance lives as long as the tab's context, it persists across action calls.
#[Signal(Scope::SESSION)] is a SESSION-scoped signal. Shared across all open tabs of the same browser session. Saving a name here broadcasts it to the other tabs.
#[Signal(Scope::GLOBAL)] is a GLOBAL-scoped signal. Shared across every connected user. The totalClicks counter increments for all users simultaneously, regardless of which tab triggered it.
#[Action] marks a public method as a client-callable action. The optional name: argument overrides the URL slug: resetTab is exposed as /_action/reset-tab.
Components + GLOBAL signals: VoteWidget is mounted three times with #[Signal(Scope::GLOBAL)] votes. SignalFactory puts the component namespace into scoped signal IDs, giving each animal its own global counter: global_cats_votes____kn, global_dogs_votes____kn, global_parrots_votes____kn.

Session Name Signal(SESSION)

Hello, Anonymous!

Open another tab: saving here updates the greeting in both tabs.

Counter Persist

Count: 0

Each click adds +1, +2, +3… The multiplier is a server-only #[Persist] property: it survives between action calls but is never sent to the client.

Global Clicks Signal(GLOBAL)

Total clicks (all users): 0

Every action on this page increments this counter. Open two browser sessions: clicking in either updates the counter in both.

Vote Widgets Component × 3

Same VoteWidget class mounted three times. Signals auto-namespace: cats.votes, dogs.votes, parrots.votes. Clicking one widget never affects the others.

0

0

0

Signals

nameInput string tab scope initial: empty string

Client-writable input bound via data-bind. Holds the typed name until saveName is called.

name string session scope initial: "Anonymous"

Promoted from nameInput by saveName. SESSION scope auto-broadcasts to all tabs of this session.

count int tab scope initial: 0

Per-tab counter. Each click adds the current multiplier: grows +1, +2, +3… proving #[Persist] survives between actions.

multiplier int server only initial: 1

Server-only instance property (not a signal). Invisible to the client. Grows by 1 on each increment call.

totalClicks int global scope initial: 0

Counts every action call by every user. GLOBAL scope auto-broadcasts to all connected sessions.

votes (VoteWidget) int global scope initial: 0

Per-animal vote counter. SignalFactory namespaces the ID: global_cats_votes____kn, global_dogs_votes____kn, global_parrots_votes____kn. Shared across all users, and back to 0 once no context uses the GLOBAL scope.

Actions

increment

Adds multiplier to count, then bumps both multiplier and totalClicks.

reset-tab

Resets count and multiplier for this tab only. Custom slug via #[Action(name: 'reset-tab')].

saveName

Copies nameInput to name (SESSION signal). Auto-broadcasts to all of this user's tabs.

vote

VoteWidget action. Independent per instance: cats.vote, dogs.vote, parrots.vote. Every user sees the same votes.

Views

composition.html.twig

Page shell. Renders all signals and three embedded VoteWidget components.

composition_vote.html.twig

VoteWidget component template. Uses the auto-namespaced votes signal and vote action.