PHPackages                             syntaxx/phpx-framework - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. syntaxx/phpx-framework

ActiveLibrary[Framework](/categories/framework)

syntaxx/phpx-framework
======================

PHPX Framework - A modern PHP framework with JSX-like syntax support

v0.3.0(1mo ago)0881MITPHPPHP &gt;=8.4

Since Oct 26Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/Syntaxx-HQ/PHPX-Framework)[ Packagist](https://packagist.org/packages/syntaxx/phpx-framework)[ RSS](/packages/syntaxx-phpx-framework/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (2)Versions (8)Used By (1)

PHPX Framework
==============

[](#phpx-framework)

**React-style components, written in PHP.** A small, isomorphic UI runtime: the same component renders on the server (SSR) and runs in the browser as **PHP compiled to WebAssembly**, then hydrates to interactive — keeping input focus and caret across re-renders.

> **Status: Technology Preview.** The core (reconciler, hooks, SSR + hydration, Suspense, router) is solid and tested, but APIs may change and the edges are sharp. Not production-ready yet.

```
function Counter($props) {
    [$count, $setCount] = useState(0);
    return (

            Count: {$count}
             $setCount($count + 1)}>+

    );
}
```

The same `Counter` is server-rendered to HTML and then hydrated in the browser — no separate JavaScript implementation, no hydration-mismatch class of bugs.

---

What it is (and isn't)
----------------------

[](#what-it-is-and-isnt)

- A **persistent-instance virtual-DOM reconciler** (Preact-class): keyed diffing, surgical DOM patching, focus/caret preservation. It is **not** React Fiber — rendering is synchronous; there is no scheduler, no time-slicing, no concurrent mode.
- **Isomorphic by construction**: one tree renders through a pluggable host config to real DOM (browser), an HTML string (server), or an in-memory fake (tests). Server and client seed the *same* hydration state.
- **Pure PHP.** The browser runtime is PHP-in-WASM via the [VRZNO](https://github.com/seanmorris/vrzno) DOM bridge; on the server it's ordinary PHP.

Installation
------------

[](#installation)

```
composer require syntaxx/phpx-framework
```

Requires **PHP 8.4+** (developed and tested on PHP 8.4). The hook functions are registered globally via Composer's `files` autoload, so they're available everywhere once the autoloader is loaded.

> Writing JSX-in-PHP (`…`) requires the **PHPX compiler**, which transforms it into the plain `Component::create(...)` calls shown below. For a full project (compiler + WASM build + dev server) start from the **PHPX starter kit** rather than wiring this package up by hand.

Components
----------

[](#components)

A component is a plain function that takes `$props` and returns an element tree.

```
function Greeting($props) {
    return Hello, {$props['name']}!;
}
```

- **Capitalized names are components** (resolved from global functions); **lowercase names are host elements** (`div`, `button`, …).
- Without the compiler, elements are created directly:

```
use Syntaxx\PHPX\Framework\Component;

Component::create('div', ['className' => 'card'], [
    Component::create('Greeting', ['name' => 'world'], []),
]);
```

- **Events** use `on*` props with PHP callables: `onClick`, `onInput`, `onKeyPress`, … (`onDoubleClick` → `dblclick`). Handlers are **delegated** — one root listener per event type — so they survive re-renders untouched.

```
 $setText($e->target->value)} />
```

Hooks
-----

[](#hooks)

All hooks are global functions (no `use` needed):

HookSignaturePurpose`useState``useState($initial, ?string $hydrationKey = null): [$value, $setValue]`Local state; supports functional updates and `===` bail-out`useEffect``useEffect(callable $fn, ?array $deps = null): void`Side effects after commit (never runs on the server)`useRef``useRef($initial = null): Ref`Stable mutable container (`$ref->current`); auto-binds to the host node when passed as a `ref` prop`useMemo``useMemo(callable $factory, array $deps): mixed`Memoized value`useCallback``useCallback(callable $cb, array $deps): callable`Memoized callback`useData``useData(string $key, callable $fetcher): [$data, $loading, $error]`Isomorphic data: server fetches + seeds, client reads the seed (fetches once if unseeded)`useSuspenseData``useSuspenseData(string $key, callable $fetcher): mixed`Returns the value or throws a `Suspension` caught by the nearest ``Standard rules of hooks apply: call them unconditionally and in the same order every render.

Server-side rendering + hydration
---------------------------------

[](#server-side-rendering--hydration)

Render to HTML on the server:

```
use Syntaxx\PHPX\Framework\{Component, ServerRenderer};

$result = ServerRenderer::render(
    Component::create('App', [], []),
    [],                                   // explicit initial state (optional)
    ['pathname' => $path, 'search' => $search]
);

echo "{$result['html']}";
echo ServerRenderer::stateScript($result['state']);   // …
```

Hydrate the same tree in the browser (PHP-in-WASM entry point):

```
use Syntaxx\PHPX\Framework\{Component, Runtime, Router};

$root = Runtime::hydrateRoot($document->getElementById('root'));
$render = fn() => $root->render(Component::create('App', [], []));
$render();
Router::start($render);   // client-side navigation re-renders without rebooting WASM
```

### Streaming SSR

[](#streaming-ssr)

`StreamRenderer::stream($component, $state, $location)` yields `shell`, `boundary`, and `close` chunks so the shell (with `` fallbacks) flushes first and boundary content streams in as data settles.

Routing
-------

[](#routing)

A minimal History-API router:

- `Router::start(callable $onChange)` — intercept internal link clicks + popstate.
- `Router::navigate(string $href)` — programmatic navigation.
- `Router::current(): array` — `['pathname' => …, 'search' => …]`.
- `Environment::location()` / `Environment::isServer()` — read location isomorphically.

Routing is flat pathname matching today (no route params or nested routes yet).

Architecture
------------

[](#architecture)

```
Component tree ─▶ Reconciler ─▶ HostConfig backend ─▶ output
                  (keyed diff,    ├─ VrznoBackend  → real DOM (browser)
                   surgical        ├─ SsrBackend    → HTML string (server)
                   patching)       └─ FakeDomBackend→ in-memory (tests)

```

The reconciler keeps persistent nodes across renders and applies only the minimal set of mutations, which is what preserves focus, caret, scroll, and media state. Hooks are bound to those persistent instances. The `HostConfig` interface lets you render to any target.

What's implemented
------------------

[](#whats-implemented)

- ✅ Components, props, children, fragments
- ✅ Hooks: `useState`, `useEffect`, `useRef`, `useMemo`, `useCallback`, `useData`, `useSuspenseData`
- ✅ Delegated events with a synthetic-event object
- ✅ Keyed reconciliation + surgical DOM patching (focus-preserving)
- ✅ `` and streaming SSR
- ✅ SSR + hydration (isomorphic, state-seeded)
- ✅ Client-side router
- ⬜ Context API, `useReducer`
- ⬜ Route params / nested routes / route data loaders
- ⬜ Concurrent / interruptible rendering

Testing
-------

[](#testing)

```
composer install
vendor/bin/phpunit
```

Tests run headlessly against an in-memory `FakeDomBackend` (no browser needed) and cover the reconciler, hooks, hydration, SSR, and Suspense.

Where this fits
---------------

[](#where-this-fits)

PHPX Framework is one module of the **Syntaxx / PHPX** ecosystem:

- **PHP-X-Parser** — JSX grammar + AST for PHP
- **PHPX-Compiler** — transforms JSX-in-PHP → plain PHP
- **PHPX-BuildTools** — build/pack/export/serve for WebAssembly projects
- **PHPX-WasmRuntimeVrzno** — the PHP-WASM browser runtime (VRZNO)
- **PHPX Framework** — this package: the component runtime

License
-------

[](#license)

MIT

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance89

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~119 days

Total

3

Last Release

57d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1073586?v=4)[Øystein Kambo Tangerås](/maintainers/kambo)[@kambo](https://github.com/kambo)

---

Top Contributors

[![kambo-1st](https://avatars.githubusercontent.com/u/6493048?v=4)](https://github.com/kambo-1st "kambo-1st (16 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/syntaxx-phpx-framework/health.svg)

```
[![Health](https://phpackages.com/badges/syntaxx-phpx-framework/health.svg)](https://phpackages.com/packages/syntaxx-phpx-framework)
```

###  Alternatives

[nineinchnick/edatatables

Grid widget for the Yii Framework, wrapper for the DataTables jQuery plugin

173.2k](/packages/nineinchnick-edatatables)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
