Working with Elements
Two-Step Factory Pattern
Element functions use a curried two-step pattern powered by FutureNode
:
-
First call — pass attributes (named arguments with IDE autocompletion):
use function Domovoy\VirtualDom\Generated\div; $future = div(id: 'main', className: 'container'); // Returns a FutureNode<DivNode> -
Second call — pass children to produce the final node:
$node = $future('Hello', $childNode, 42); // Returns a DivNode
Typically written as a single chained expression:
$node = div(id: 'main', className: 'container')(
p()('First paragraph'),
p()('Second paragraph'),
);
Typed Attributes
Each element has a generated attributes class with typed properties matching the W3C IDL specification. Your IDE provides autocompletion for every attribute:
use function Domovoy\VirtualDom\Generated\input;
// PHPStan checks that 'type' is ?string, 'disabled' is ?bool, etc.
$node = input(type: 'email', required: true, placeholder: 'you@example.com');
IDL property names are mapped to HTML attribute names automatically
(className
becomes class
, htmlFor
becomes for
).
Void Elements
Void elements (br
, img
, input
, hr
, meta
, link
, ...)
return a Node
directly — no second call needed:
use function Domovoy\VirtualDom\Generated\{img, br, hr};
$image = img(src: '/logo.png', alt: 'Logo');
$break = br();
$rule = hr();
Children Auto-Wrapping
String and numeric values among children are automatically wrapped in
TextNode
with HTML escaping:
$node = p()('Price: ', 42, ' EUR');
// <p>Price: 42 EUR</p>
null
children are silently ignored, which is useful for conditional rendering:
$node = div()(
$showHeader ? h1()('Title') : null,
p()('Content'),
);
Closures are invoked and their return values used as child nodes:
$node = div()(
fn () => p()('Lazy child'),
);
Raw HTML
To insert unescaped HTML, use the raw()
helper:
use function Domovoy\VirtualDom\raw;
$node = div()(raw('<strong>Bold text</strong>'));
The caller is responsible for ensuring the HTML is safe. This is intentional — Domovoy escapes everything else by default.
DOCTYPE Declaration
use function Domovoy\VirtualDom\doctype;
$dtd = doctype(); // <!DOCTYPE html>
$dtd = doctype('xml'); // <!DOCTYPE xml>
Fragments
Group multiple nodes without producing an HTML tag:
use function Domovoy\VirtualDom\fragment;
$group = fragment(
p()('First'),
p()('Second'),
);
// Renders as: <p>First</p><p>Second</p>
Fragments are useful for returning multiple nodes from a template or function.