Getting Started
Counter
Assigns become props; clicks become handle_event
- props
- phx-click
- local state
Key concepts
Every assign that is not a reserved name is passed to React as a prop. count
lives on the server; the step slider lives in React's useState, and the server never sees it.
import React, { useState } from "react";
import { useLiveReact } from "live_react";
export function Counter({ count }) {
const { pushEvent } = useLiveReact();
const [step, setStep] = useState(1);
return (
<div className="flex items-center gap-4">
<button
className="rounded-md border px-3 py-1"
onClick={() => pushEvent("set_count", { value: count - step })}
>
−{step}
</button>
<span className="min-w-16 text-center text-2xl font-semibold">{count}</span>
<button
className="rounded-md border px-3 py-1"
onClick={() => pushEvent("set_count", { value: count + step })}
>
+{step}
</button>
{/* step lives only in React — the server never sees it */}
<label className="ml-4 flex items-center gap-2 text-sm">
step
<input
type="range"
min="1"
max="10"
value={step}
onChange={(e) => setStep(Number(e.target.value))}
/>
{step}
</label>
</div>
);
}
How it works
Clicking a button calls pushEvent("set_count", …), which reaches
handle_event/3
exactly as phx-click
would. The server
reassigns count, LiveReact diffs the props and sends only what
changed, and React re-renders.
Dragging the slider changes nothing on the server — that is the point. Local UI state stays local, and only what the server owns round-trips.