PHPackages                             domovoy/domovoy - 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. [Templating &amp; Views](/categories/templating)
4. /
5. domovoy/domovoy

ActiveLibrary[Templating &amp; Views](/categories/templating)

domovoy/domovoy
===============

Type-safe virtual DOM for PHP 8.4+ with typed HTML elements generated from W3C specs

v0.1.0(3mo ago)2106MITPHPPHP ^8.4CI passing

Since Mar 9Pushed 3mo agoCompare

[ Source](https://github.com/mlavrinenko/domovoy)[ Packagist](https://packagist.org/packages/domovoy/domovoy)[ Docs](https://github.com/mlavrinenko/domovoy)[ RSS](/packages/domovoy-domovoy/feed)WikiDiscussions main Synced 3w ago

READMEChangelogDependencies (10)Versions (2)Used By (0)

Domovoy
=======

[](#domovoy)

Type-safe virtual DOM library that generates typed HTML element classes from W3C specifications.

A foundation for a templating engine that:

- runs on pure PHP — no separate template language (Twig), no compilation step;
- guarantees input types — PHPStan validates signatures at max level;
- solves the "magic helpers" problem (Plates) — dependencies are injected explicitly via constructor.

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

[](#installation)

```
composer require domovoy/domovoy
```

Quick Start
-----------

[](#quick-start)

### Building HTML

[](#building-html)

Every HTML element is represented by a function with typed attributes. Calls are two-step — first attributes, then children:

```
use function Domovoy\VirtualDom\Generated\div;
use function Domovoy\VirtualDom\Generated\ul;
use function Domovoy\VirtualDom\Generated\li;
use function Domovoy\VirtualDom\Generated\img;
use Domovoy\VirtualDom\Renderer\HtmlRenderer;

$node = div(id: 'main', className: 'container')(
    ul(className: 'list')(
        li()('First'),
        li()('Second'),
    ),
    img(src: '/logo.png', alt: 'Logo'),  // void element — no second call
);

$html = (new HtmlRenderer())->render($node);
// FirstSecond
```

String and numeric values among children are automatically wrapped in `TextNode` with HTML escaping. To insert raw HTML, use `raw()`:

```
use function Domovoy\VirtualDom\raw;

div()(raw('Bold text'));
```

### Templates

[](#templates)

A template is a callable class implementing `Template`:

- **Constructor** — service dependencies (wired by a DI container).
- **`__invoke()`** — data contract (validated by PHPStan) — returns a `Node`.

```
use Domovoy\Template\Template;
use Domovoy\VirtualDom\Contract\Node;
use function Domovoy\VirtualDom\Generated\div;
use function Domovoy\VirtualDom\Generated\h3;
use function Domovoy\VirtualDom\Generated\span;

final readonly class ProductCard implements Template
{
    public function __construct(private PriceFormatter $formatter) {}

    public function __invoke(Product $product): Node
    {
        return div(className: 'card')(
            h3()($product->name),
            span(className: 'price')($this->formatter->format($product->price)),
        );
    }
}
```

### Layout Inheritance via Blocks

[](#layout-inheritance-via-blocks)

`BlockRegistry` is an immutable registry of named blocks for composing layouts:

```
use Domovoy\Template\BlockRegistry;
use Domovoy\Template\Template;
use Domovoy\VirtualDom\Contract\Node;
use function Domovoy\VirtualDom\Generated\{html, head, title, body, header, main, footer, nav, p};

final readonly class BaseLayout implements Template
{
    public function __invoke(string $title, BlockRegistry $blocks): Node
    {
        return html(lang: 'en')(
            head()(title()($title)),
            body()(
                header()($blocks->getOrDefault('nav', nav()())),
                main()($blocks->get('content')),
                footer()($blocks->getOrDefault('footer', p()('Default footer'))),
            ),
        );
    }
}
```

A child layout overrides parent blocks:

```
final readonly class AdminLayout implements Template
{
    public function __construct(private BaseLayout $base) {}

    public function __invoke(string $title, BlockRegistry $blocks): Node
    {
        return ($this->base)(
            "Admin — {$title}",
            $blocks->set('nav', nav()(
                a(href: '/admin')('Dashboard'),
                a(href: '/admin/users')('Users'),
            )),
        );
    }
}
```

A page assembles the layout with data:

```
final readonly class DashboardPage implements Template
{
    public function __construct(
        private AdminLayout $layout,
        private ProductCard $card,
    ) {}

    public function __invoke(ProductCollection $products): Node
    {
        $card = $this->card;

        return ($this->layout)(
            'Dashboard',
            BlockRegistry::create()
                ->set('content', div(className: 'dashboard')(
                    h1()('Product Dashboard'),
                    ...array_map(
                        static fn (Product $p): Node => $card($p),
                        $products->toArray(),
                    ),
                )),
        );
    }
}
```

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

[](#architecture)

### Code Generation from W3C Specs

[](#code-generation-from-w3c-specs)

The library parses official W3C JSON specifications ([elements](https://w3c.github.io/webref/ed/elements/html.json), [IDL](https://w3c.github.io/webref/ed/idlparsed/html.json)) and generates:

- **Attribute classes** (`DivAttributes`, `InputAttributes`, ...) — one per interface, with typed properties from WebIDL;
- **Node classes** (`DivNode`, `InputNode`, ...) — readonly wrappers implementing `Node`;
- **`NodeName` enum** — all HTML tags;
- **`node_fn.php`** — helper functions (`div()`, `input()`, ...) for every element.

Generation is triggered via `just generate`.

### Virtual DOM

[](#virtual-dom)

```
Node (interface)
├── TextNode          — escaped text
├── RawNode           — raw HTML
└── *Node (generated) — typed HTML elements
    ├── attributes: NodeAttributes  — typed element attributes
    └── children(): ?NodeCollection — child nodes (null for void elements)

```

`HtmlRenderer` traverses the tree and produces an HTML string with proper escaping, boolean attribute handling, and IDL-to-HTML name mapping (`className` → `class`, `htmlFor` → `for`).

### FutureNode — Two-Step Factory

[](#futurenode--two-step-factory)

Element functions return a `FutureNode` — a curried constructor:

1. First call — attributes: `div(id: 'main', className: 'active')`
2. Second call — children: `(...)('Hello', $child, 42)`

Void elements (`br`, `img`, `input`, ...) return a `Node` directly, with no second call needed.

Development
-----------

[](#development)

See [CONTRIBUTING.md](CONTRIBUTING.md) for setup instructions and development workflow.

License
-------

[](#license)

[MIT](./LICENSE)

Future
------

[](#future)

The current package combines virtual DOM abstraction and templating functionality. Before API stabilization, these concerns will likely be split into two separate packages:

- `domovoy`: Virtual DOM abstraction
- `domoview`: Templating system

This is not yet done just for the sake of simplicity.

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance79

Regular maintenance activity

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

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

Unknown

Total

1

Last Release

109d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ab1bbc009e4fa562e3c976d1d1be7b5a61e2199b28e43828632eb8c72f827631?d=identicon)[mlavrinenko](/maintainers/mlavrinenko)

---

Top Contributors

[![mlavrinenko](https://avatars.githubusercontent.com/u/15840214?v=4)](https://github.com/mlavrinenko "mlavrinenko (27 commits)")

---

Tags

phptemplate-enginetemplatingvirtual-domvirtual-dom-libraryvirtualdomhtmltemplateW3Ccode-generationtype-safephp84virtual-dom

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/domovoy-domovoy/health.svg)

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

###  Alternatives

[phpoffice/phpword

PHPWord - A pure PHP library for reading and writing word processing documents (OOXML, ODF, RTF, HTML, PDF)

7.6k37.4M220](/packages/phpoffice-phpword)[latte/latte

☕ Latte: the intuitive and fast template engine for those who want the most secure PHP sites. Introduces context-sensitive escaping.

1.3k16.0M748](/packages/latte-latte)[moonshine/moonshine

Laravel administration panel

1.3k239.9k76](/packages/moonshine-moonshine)[phug/phug

Pug (ex-Jade) facade engine for PHP, HTML template engine structured by indentation

66297.7k14](/packages/phug-phug)[sitegeist/fluid-components

Encapsulated frontend components with Fluid's ViewHelper syntax

55354.1k3](/packages/sitegeist-fluid-components)[epic-64/elem

A fluent, type-safe PHP library for building HTML documents using the DOM

2811.4k](/packages/epic-64-elem)

PHPackages © 2026

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