Live Components
Live components are stateful, server-rendered widgets that update in the browser without you writing any JavaScript. Think of a counter, a like button, or a multi-step form: the state lives on the server, the user clicks something, the server re-renders, and Fast swaps the new HTML into the page. It is the highest tier of Fast's real-time toolkit.
A first component
A component is a class extending Fast\Live\Component. Its public properties are its state, its methods are actions the browser can call, and render() produces its HTML:
final class Counter extends Fast\Live\Component
{
public int $count = 0;
public function increment(): void
{
$this->count++;
}
public function render(): string
{
return '<button f-click="increment">' . e($this->count) . '</button>';
}
}
The f-click="increment" attribute wires the button to the increment() method. When the user clicks, Fast calls that method on a rehydrated instance, re-renders, and updates the DOM. No fetch calls, no event listeners, no build step.
Registering and rendering
Register your components in a name-to-class allowlist, then render one by its registered name. You also need live_scripts() on the page to load the tiny client runtime:
use_components(env_string('APP_KEY'), ['counter' => Counter::class]);
get('/', fn (): string => live_scripts() . component('counter'));
The first argument to use_components() is a secret key used to protect component state. Use a stable secret from your configuration; do not generate a fresh one on every boot, or existing component state on live pages would stop decrypting.
How state travels
This is the clever part. A component instance does not live on the server between requests. Instead, its public property values are encrypted, authenticated, and carried along with the rendered HTML. When an action fires, Fast decrypts that state, rebuilds the component on whichever worker handles the request, runs your method, and re-renders. Nothing is stored server-side, so it scales across every worker for free.
Only certain properties are carried, and Fast is strict about it:
- Public, scalar, typed properties (
int,float,bool,string, and their
nullable variants) are the client-carried state.
- Every public, non-static method you declare on the component is a
browser-callable action, except mount(), render(), abstract methods, and magic methods.
Keep server-only logic private
Because every public method is callable from the browser, anything you do not want a user to invoke directly must be protected or private:
final class Invoice extends Fast\Live\Component
{
public int $total = 0;
public function recalculate(): void // browser can call this
{
$this->total = $this->lineItemSum();
}
private function lineItemSum(): int // browser CANNOT call this
{
// internal helper
}
}
Treat public methods as a public API surface. A server-only operation that lands in a public method becomes an endpoint anyone can hit. Keep helpers private, and always escape rendered state with
e(), because a component can rewrite arbitrary parts of the page.
Security in short
Component state is encrypted and authenticated, so a user cannot tamper with the carried values. When sessions are enabled, components also get CSRF and session binding, and same-authority checks apply regardless. The one thing you own is the secret key: keep it stable and out of your static roots.
Components are the heavy tier. When you just want to swap some HTML on submit, the lighter Fragments tier is often all you need.