Comparisons
php-via is a PHP framework. If you are choosing between it and Livewire, htmx, Inertia or LiveView, this page shows what each one does well and where php-via falls short.
Feature comparison
~ means partial support. Hover, tap or focus a mark to read the details.
| Feature | php-via | Livewire | htmx + Alpine | Inertia.js | LiveView |
|---|---|---|---|---|---|
| Native real-time push | ✓One SSE stream per tab carries every update, and any action, timer or broadcast can push to it. | ~Server push needs Laravel Echo plus a broadcaster such as Reverb, Pusher, Ably or Mercure. Livewire alone offers polling and in-request streaming with wire:stream. | ~The SSE extension streams HTML into the page, but the server side is yours to build. | ✗No push channel of its own: use polling, or a separate broadcaster with Laravel Echo. | ✓A persistent WebSocket carries diffs from the server. |
| Multiplayer / shared state | ✓Scoped signals: one line shares state with everyone on a route, in a session, in a room or on the whole site. | ~No shared server-side state. Multiplayer UIs broadcast Laravel events through Echo and re-render from the database. | ✗ | ✗ | ✓Phoenix PubSub broadcasts between LiveView processes. |
| Scoped state system | ✓TAB, ROUTE, SESSION, GLOBAL, or your own scopes like room:42, with wildcard broadcasts. | ✗ | ✗ | ✗ | ~PubSub topics achieve similar results without a declarative scope system. |
| Presence (who is online) | ✓getClients() and countClients() see the tabs on every worker, onClientConnect() and onClientDisconnect() run as tabs come and go, and a closed tab disconnects at once. | ~Laravel presence channels, through Echo and a broadcaster. | ✗ | ✗ | ✓Phoenix.Presence tracks users across nodes. |
| No JS to author | ✓No JS files, components or client state. Datastar attributes take one-line expressions such as @post(url). | ✓Typical components use only wire: directives and Blade. Alpine ships bundled for optional client-side behaviour. | ~htmx requests are plain HTML attributes, but Alpine state and behaviour are inline JavaScript expressions. | ✗Pages are React, Vue or Svelte components. | ~JS hooks are needed for some client-side interactions. |
| No build step | ✓php-via serves its own datastar.js. No bundler, no npm. | ✓Livewire injects its script and Alpine automatically. The documented Echo setup for push uses npm and Vite. | ✓ | ✗npm and Vite build the client. | ~No Node.js: Mix runs standalone esbuild and Tailwind binaries, but the app still bundles its assets. |
| File uploads | ✓Multipart form submission with Datastar's contentType: 'form' modifier. Files arrive in $c->file(). | ✓Temporary uploads with previews, progress events, validation and direct-to-S3 uploads. | ✓ | ✓ | ✓Built-in progress, drag and drop, previews and direct-to-cloud uploads. |
| Form validation | ~No validation API: validate in the action with plain PHP or any validator library, and send per-field errors back as signals or re-rendered HTML. With contentType: 'form', the browser's HTML5 checks run first. | ✓Laravel's validator through #[Validate] attributes or form objects. | ~HTML5 checks in the browser. Server-side errors come back as HTML from your backend. | ✓Server validation errors arrive as props from the backend adapter. | ✓Ecto changesets validate on change and on submit. |
| Auth / middleware | ✓PSR-15 middleware per route or group. Actions are bound to the session and check the Origin header. | ✓ | ✓Provided by the backend you pair it with. | ✓ | ✓ |
| Built-in debugging / observability | ✓The Dev Bar ships with the framework and turns on with dev mode: a request-trace waterfall, the raw SSE patches, and live signal, scope and log panels. | ~No built-in inspector. Laravel Debugbar detects Livewire components, plus Telescope and community browser extensions. | ~Console event logging plus third-party htmx and Alpine DevTools extensions, with no in-page inspector. | ✓The first-party Inertia DevTools browser panel records every visit with its props and headers (Laravel adapter). | ✓Phoenix LiveDashboard (real-time metrics, requests, processes) plus HEEx debug annotations. |
| Multi-core on one server | ✓withWorkerNum(), with SwooleBroker by default: scoped signals, GlobalState, session data and the client list live in shared memory, and an action reaches its tab on any worker. | ✓ | ✓ | ✓ | ✓ |
| Multi-server | ~RedisBroker or NatsBroker carries broadcasts between servers. Use sticky sessions and keep shared state in a database. | ✓ | ✓ | ✓ | ✓Distributed PubSub and Presence across nodes. |
| Runs without a long-running process | ✗Needs a long-running OpenSwoole 26 process behind a reverse proxy. No PHP-FPM or shared hosting. | ✓Plain PHP-FPM. Real-time push adds a Reverb process. | ✓Any backend. Under PHP-FPM every open SSE stream ties up a worker. | ✓Plain PHP-FPM. Server-side rendering adds a Node.js process. | ✗Runs as a long-lived BEAM release. |
| No server memory per open tab | ✗Every open tab holds its context and its SSE stream in server memory. | ✓Component state travels with each request in a checksummed snapshot. | ✓ | ✓ | ✗Each LiveView is a BEAM process that holds its state. |
| Ecosystem & community | Early | Large | Large | Large | Large |
| Framework required | Nophp-via is its own server. Next to a Laravel or Symfony app it runs as a separate process. | Laravel | No | React, Vue or SveltePlus a server adapter: Laravel is first-party, and community adapters cover Rails, Phoenix, Django, Symfony and more. | Phoenix |
| Language | PHP | PHP | PHP + JS | PHP + JS | Elixir |
| Runtime | OpenSwoole 26 | PHP-FPM or Octane | Any backend | PHP-FPM, Node for SSR | BEAM VM |
One connection per tab, compressed as a stream
Livewire and Inertia make a new HTTP request when something changes, and htmx does
the same unless you wire up its SSE extension. php-via, like LiveView, keeps one connection open
per tab and pushes the changed fragment down it, not full re-renders or state trees.
With withBrotli() the stream is compressed as a whole, so a patch that repeats
earlier markup costs a few bytes: the Game of Life frames compress about 30:1 at the default
level. On slow or metered connections (mobile, satellite) that gap is significant.
The trade-off is a persistent OpenSwoole process instead of stateless PHP-FPM, and server memory for every open stream: a busy stream's Brotli encoder grows to about 9 MB at the default level 4, or about 0.6 MB at level 1.
Full explanation in Design decisions →
vs Livewire
Livewire is the most direct PHP alternative. It's mature, well-documented, and has a large ecosystem.
Choose Livewire when: you're in a Laravel project, need built-in form validation, rich file uploads (previews, progress, direct-to-S3), auth scaffolding, plain PHP-FPM hosting, or the safety net of a large community and years of production use.
Choose php-via when: you need real-time push without adding Echo and a broadcaster, or multiplayer state shared across users without pub/sub infrastructure, and you can run a long-running OpenSwoole process and accept an early-stage library.
vs htmx + Alpine
htmx + Alpine is not a framework but a composition of two libraries. It works with any PHP backend and lets you stay on PHP-FPM.
Choose htmx + Alpine when: you want to add interactivity incrementally to an existing app, need full control of the request/response cycle, or aren't comfortable running a persistent OpenSwoole process.
Choose php-via when: you want server state that pushes its own updates to every affected tab, and the scope system to decide who receives them.
vs Inertia.js
Inertia is a protocol layer that connects a server-side framework (typically Laravel) to a React, Vue, or Svelte frontend. It requires a build step and JavaScript knowledge.
Choose Inertia when: your team already knows React/Vue/Svelte, you want SPA-style navigation with a PHP backend, or you need the full power of a JS component ecosystem.
Choose php-via when: you want to stay in PHP entirely and push DOM patches server-side, with no client-side routing and no JavaScript build pipeline.
vs Phoenix LiveView
Phoenix LiveView is the clearest inspiration for php-via's architecture: persistent server-side state over a live connection, with DOM diffing pushed to the client.
Choose LiveView when: you work in Elixir, need the fault tolerance and distributed capabilities of the BEAM VM, or require production-grade multi-server deployment with PubSub baked in.
Choose php-via when: you work in PHP. The scope system (Scope::ROUTE,
Scope::SESSION, Scope::GLOBAL) intentionally mirrors LiveView's
PubSub model with a simpler declarative API.
The Datastar ecosystem
Datastar is the underlying SSE + DOM-morphing library that php-via builds on top of. Several other frameworks in other languages use the same protocol:
- Datapages (Go + Templ): a code-generation framework with built-in CSRF, typed auth, a full CLI, and NATS-based multi-instance broadcasting. The better choice if you're working in Go.
- Stario (Python 3.14+): a stateless async handler framework with rich HTTP primitives (cookies, redirects, compression), full HTTP method routing, and request tracing. The better choice if you're working in Python or need fine-grained HTTP control.
These are different languages targeting different teams. Comparing php-via to Datapages or Stario is less useful than comparing it to Livewire: you don't switch languages because of a framework comparison.
Next steps
- Scopes: the state scoping model that differentiates php-via
- Components: sub-contexts with isolated state
- API Reference: complete method signatures