Skip to content

HTML Components

Greg Bowler edited this page Apr 12, 2026 · 11 revisions

Components let us keep a chunk of HTML in its own file and pull it into the page using a custom element such as <profile-summary>.

This is particularly helpful when a section of UI becomes awkward to maintain inline, or when the same structure appears on several pages.

A first component

Page HTML:

<h2>Account details</h2>
<profile-summary />

Component file:

_component/profile-summary.html

<dl>
	<dt>Name</dt>
	<dd data-bind:text="name">Guest</dd>

	<dt>Email</dt>
	<dd data-bind:text="email">guest@example.com</dd>
</dl>

PHP:

use GT\DomTemplate\ComponentExpander;
use GT\DomTemplate\PartialContent;

$expander = new ComponentExpander(
	$document,
	new PartialContent(__DIR__ . "/_component"),
);

$expandedComponents = $expander->expand();

If we do this, the <profile-summary> element stays in the document, but its inner HTML is replaced with the component file contents.

Component names

The W3C have decided that custom component names must contain a hyphen.

Good examples:

  • <profile-summary>
  • <calendar-control>
  • <shop-item-card>

This keeps custom elements distinct from native HTML elements.

Components in subdirectories

The src attribute lets us load from a subdirectory.

Page HTML:

<date-picker src="calendar" />
<date-picker src="christmas" />

Component files:

  • _component/calendar/date-picker.html
  • _component/christmas/date-picker.html

That gives us a tidy way to organise larger component libraries without inventing a separate naming system.

Recursive components

Components can contain other components.

So if profile-summary.html itself contains <avatar-image />, DomTemplate will keep expanding until there are no more matching component files.

This makes it practical to build layered interfaces from smaller pieces.

ComponentBinder

Once a component has been expanded, we often want to bind data only within that one component, and not leak data binding out to the rest of the document. That is what ComponentBinder is for.

use GT\DomTemplate\ComponentBinder;

$componentElement = $document->querySelector("profile-summary");
$componentBinder = new ComponentBinder($document);
$componentBinder->setDependencies(
	$elementBinder,
	$placeholderBinder,
	$tableBinder,
	$listBinder,
	$listElementCollection,
	$bindableCache,
);
$componentBinder->setComponentBinderDependencies($componentElement);

$componentBinder->bindData([
	"name" => "Ada",
	"email" => "ada@example.com",
]);

The API is intentionally the same as DocumentBinder, but the scope is locked to one component subtree.

Note

In WebEngine applications, you can use the Binder helper class, which will be automatically instantiated as a DocumentBinder when in page context, and ComponentBinder when in component context.

Selector contexts inside a component

ComponentBinder also accepts selector string contexts:

$componentBinder->bindKeyValue("title", "Orders", ".heading");

If the selector does not match inside the component, it throws ContextElementNotFoundException.

Hidden __component fields in POST forms

When a component contains a <form method="post">, DomTemplate automatically prepends a hidden input named __component with the component tag name.

That gives server-side code a tidy way to tell which component submitted the form, and makes custom routing easier.


Components handle reusable fragments of HTML, but Partials are used for whole-page templates.

Clone this wiki locally