Documentation menu

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-viaLivewirehtmx + AlpineInertia.jsLiveView
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