php-web-component-ssr · packagist slogsdon/web-component-ssr

Template in. HTML out.

One PHP file of functions. It reads a custom element's template from disk, fills the template's slots with whatever data you pass, and prints two things: a placeholder for the browser to find, and the template the client half will clone.

Quoted from slogsdon/php-web-component-ssr at commit 00be84d (2026-05-08). The output in the example was produced by running the package's functions on PHP 8.5.11 while building this page; the demo commands were not run.

the two functions you call

getTemplate($name, $data = null)
Prints the server-side half: a div carrying data-template="$name", the template's children moved into it, and — when you pass data — a URL-encoded data-initial-data attribute.
getClientTemplate($name, $data = null)
Prints the client-side half: the raw template file as a template element with id="template-$name". The hydration library looks the element up by that id.
templateLocation($directory = null)
Tells the package where template files live. It keeps the path in a static variable, so call it once before rendering; with no argument it returns the current directory.

Both readers funnel through getTemplateSource($name, $data = []), which includes <template-location>/<name>.html with $data extracted into scope, so a template file is ordinary PHP-flavoured HTML. A missing template file returns an empty string rather than failing.

install

The README's requirements list is short: PHP 7.1+, Composer for the autoloader only, and web-component-hydration for the client half. composer.json requires ext-dom and ext-json — no runtime libraries — and PSR-4 autoloads WebComponentSSR\ from src/, loading src/functions.php eagerly.

There is no tagged release. Packagist lists the package as slogsdon/web-component-ssr with exactly one version, dev-master, pointing at the same commit this page quotes. The README does not give a composer require line, so none is printed here: a stable constraint would not resolve.

What the README does document is running the demo. Both lines are quoted exactly:

cd examples/simple && yarn install
cd ../.. && composer start

The second line runs the package's Composer script, which is @php -S 0.0.0.0:5000 -t examples/simple: PHP's built-in server on port 5000, serving the demo directory. composer test runs Psalm, the only dev dependency (vimeo/psalm ^3.4).

The yarn install is for the demo's own examples/simple/js/package.json, which depends on web-component-hydration@^1.0.0; its lockfile resolves that to 1.0.5. The demo imports the package by relative path — ./node_modules/web-component-hydration/helpers.js — so there is no bundler and no build step.

the demo, end to end

examples/simple/index.php sets the template directory and then renders x-list-item inside x-list, hands the page a client template for each, and loads the module script. The two calls, quoted:

<?php
WebComponentSSR\templateLocation(__DIR__ . '/js/templates');
?>

<!-- server rendered -->
<?= getTemplate('x-list', getTemplate('x-list-item', ['label' => 'content', 'foo' => 'bar'])); ?>

<!-- client template -->
<?= getClientTemplate('x-list'); ?>
<?= getClientTemplate('x-list-item'); ?>

The templates are plain HTML files in examples/simple/js/templates/, x-list.html holding <ul><slot></slot></ul> and x-list-item.html holding <li><slot name="label"></slot></li>, each wrapped in a <template>.

What it produced. Running those two calls on PHP 8.5.11, with templateLocation() pointed at the demo's templates, printed the following. It is verbatim apart from a trailing newline; the long data-initial-data value is one line in the output.

<div data-template="x-list" data-initial-data="%3Cdiv+data-template%3D%22x-list-item%22+data-initial-data%3D%22%257B%2522label%2522%253A%2522content%2522%252C%2522foo%2522%253A%2522bar%2522%257D%22%3E%0A++%3Cli%3E%0A++++%3Cslot+name%3D%22label%22%3Econtent%3C%2Fslot%3E%0A++%3C%2Fli%3E%0A%3C%2Fdiv%3E%0A">
  <ul>
    <slot><div data-template="x-list-item" data-initial-data="%7B%22label%22%3A%22content%22%2C%22foo%22%3A%22bar%22%7D">
  <li>
    <slot name="label">content</slot>
  </li>
</div>
</slot>
  </ul>
</div>

And the client template for x-list-item:

<template id="template-x-list-item">
  <li>
    <slot name="label"></slot>
  </li>
</template>

reading the output

The data shape decides which slots fill. A string fills only the unnamed slot, inserted as an XML fragment — that is why the outer x-list slot holds the child element's markup. An array fills only named slots, one text node each, and leaves an unnamed slot empty. The demo passes foo => 'bar' to x-list-item, whose template declares only slot name="label", so that key goes unused here; the hydration library's README shows a template that declares both.

The placeholder copies the template, minus its id. getTemplate() moves the template's children into the new div and copies every attribute except id. So the content is real, visible light DOM until a script replaces it.

Encoding. String data is passed to urlencode() before it becomes data-initial-data; arrays go through json_encode() first. The client half undoes both.

A deprecation notice on current PHP. The same run printed mb_convert_encoding(): Handling HTML entities via mbstring is deprecated, naming src/CustomHTMLElement.php line 38. The output above was still produced. The README's stated floor is PHP 7.1; this was PHP 8.5.11, and nothing in the repository was changed to silence the notice.

No declarative shadow DOM. The printed markup contains no <template shadowrootmode>; the shadow root arrives later, when the client half constructs the element.

where to go