PHPackages                             gin0115/elmishphp-html - 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. gin0115/elmishphp-html

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

gin0115/elmishphp-html
======================

A functional based library for creating HTML in PHP, heavily inspired by the ELM HTML package

1.0.0(1mo ago)00GPL-2.0-or-laterPHPPHP &gt;=8.2.0CI passing

Since May 21Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/gin0115/ElmishPHP-HTML)[ Packagist](https://packagist.org/packages/gin0115/elmishphp-html)[ Docs](https://github.com/gin0115/ElmishPHP-HTML)[ RSS](/packages/gin0115-elmishphp-html/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (1)Dependencies (8)Versions (5)Used By (0)

Elmish PHP — HTML
=================

[](#elmish-php--html)

A functional library for creating HTML in PHP, heavily inspired by Elm's `Html` package. Each element is a typed value object with `__toString` — compose them with curried functions, render by stringification.

[![Latest Stable Version](https://camo.githubusercontent.com/ec362f8c0761122fe10d81d73c301903d95f3c6d57f24b885997d1bca2d308b1/68747470733a2f2f706f7365722e707567782e6f72672f67696e303131352f656c6d6973687068702d68746d6c2f76)](https://packagist.org/packages/gin0115/elmishphp-html) [![Total Downloads](https://camo.githubusercontent.com/7dceca9d965e84c2d6bed9369ef4327b5ffb828d9ba4f70874ed6f3a8d282094/68747470733a2f2f706f7365722e707567782e6f72672f67696e303131352f656c6d6973687068702d68746d6c2f646f776e6c6f616473)](https://packagist.org/packages/gin0115/elmishphp-html) [![Latest Unstable Version](https://camo.githubusercontent.com/5953875dd91b2dfa3e9a48535c35f23e13b7abd0db5108945fef252a55011488/68747470733a2f2f706f7365722e707567782e6f72672f67696e303131352f656c6d6973687068702d68746d6c2f762f756e737461626c65)](https://packagist.org/packages/gin0115/elmishphp-html) [![License](https://camo.githubusercontent.com/30540b6244c988f1cfc4419ff63128d227f6350af2c77e72dc68ba51ec53ee11/68747470733a2f2f706f7365722e707567782e6f72672f67696e303131352f656c6d6973687068702d68746d6c2f6c6963656e7365)](https://packagist.org/packages/gin0115/elmishphp-html) [![PHP Version Require](https://camo.githubusercontent.com/4d367a769ff4a3e8a6abb6813c9cef4333e3c048d2e4ca98c85410590d6ead45/68747470733a2f2f706f7365722e707567782e6f72672f67696e303131352f656c6d6973687068702d68746d6c2f726571756972652f706870)](https://packagist.org/packages/gin0115/elmishphp-html)[![GitHub contributors](https://camo.githubusercontent.com/97babb504f8d99c1fb981737ce5b1e653cdef0e40226c8ae69f8ac29f9c11d45/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f67696e303131352f456c6d6973685048502d48544d4c3f6c6162656c3d436f6e7472696275746f7273)](https://camo.githubusercontent.com/97babb504f8d99c1fb981737ce5b1e653cdef0e40226c8ae69f8ac29f9c11d45/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636f6e7472696275746f72732f67696e303131352f456c6d6973685048502d48544d4c3f6c6162656c3d436f6e7472696275746f7273)[![GitHub issues](https://camo.githubusercontent.com/bd1ae57ca89b2f7c07f04fc17bc5265728707c07d1eaa58cd838ee5dc3ce7564/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732d7261772f67696e303131352f456c6d6973685048502d48544d4c)](https://camo.githubusercontent.com/bd1ae57ca89b2f7c07f04fc17bc5265728707c07d1eaa58cd838ee5dc3ce7564/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6973737565732d7261772f67696e303131352f456c6d6973685048502d48544d4c)[![PHP — PHPUnit + PHPStan Level 8](https://github.com/gin0115/ElmishPHP-HTML/actions/workflows/php.yml/badge.svg)](https://github.com/gin0115/ElmishPHP-HTML/actions/workflows/php.yml)[![E2E (Playwright)](https://github.com/gin0115/ElmishPHP-HTML/actions/workflows/e2e.yml/badge.svg)](https://github.com/gin0115/ElmishPHP-HTML/actions/workflows/e2e.yml)[![codecov](https://camo.githubusercontent.com/d3a60bac33831e1a041a65186d7f571142999d21c71d0e8444aef37e14689877/68747470733a2f2f636f6465636f762e696f2f67682f67696e303131352f456c6d6973685048502d48544d4c2f67726170682f62616467652e7376673f746f6b656e3d4b305638563745483951)](https://codecov.io/gh/gin0115/ElmishPHP-HTML)

Why
---

[](#why)

Erm, next question........ok fine, why not. I really enjoyed ELMs approach to creating HTML and have played around with this idea before (Functional WP Plugin).

Install
-------

[](#install)

```
composer require gin0115/elmishphp-html
```

Then the usual `require 'vendor/autoload.php'` and you're good to go.

How it works
------------

[](#how-it-works)

Each HTML tag is a curried function. The first call passes attributes, the second passes children. Every function returns a typed object that knows how to render itself.

```
use function Gin0115\ElmishPHP\HTML\div;
use function Gin0115\ElmishPHP\HTML\span;
use function Gin0115\ElmishPHP\HTML\p;
use function Gin0115\ElmishPHP\HTML\text;

echo div(['id' => 'wrap', 'class' => 'card'])(
    p()(text('Hello, '), span(['class' => 'name'])(text('world'))),
);
```

```
Hello, world
```

### Elm-style formatting

[](#elm-style-formatting)

For longer trees, leading-comma style mirrors `elm-format`:

```
div ([ 'id' => 'wrap', 'class' => 'card' ])
    ( p ()
        ( text('Hello, ')
        , span ([ 'class' => 'name' ])(text('world'))
        )
    );
```

### Text content

[](#text-content)

escapes?use for`text('...')`yesthe default for any string`raw('x')`nopre-rendered HTML you trustbare `'string'`yes (auto)shorthand for `text('string')` as a child```
echo div()(text('alert(1)'));
// &lt;script&gt;alert(1)&lt;/script&gt;

echo div()(raw('bold'));
// bold
```

### Attributes

[](#attributes)

Attributes are an associative array. Three forms:

```
div([
    'id'        => 'foo',   // standard key=value — value is HTML-escaped
    'data-flag' => null,    // null value → bare flag attribute
    'data-other',           // positional entry → bare flag attribute
])(text('hi'));
```

```
hi
```

All attribute *values* are HTML-escaped. Keys are not (they're under your control).

### Void elements

[](#void-elements)

Void elements take attributes only — **no second call for children**:

```
echo br();                                            //
echo img(['src' => 'logo.png', 'alt' => 'logo']);     //
echo input(['type' => 'text', 'name' => 'q', 'required']);
//
```

The void set: `br`, `hr`, `img`, `input`, `wbr`, `col`, `source`, `track`.

### Custom tags via `node()`

[](#custom-tags-via-node)

For anything not in the built-in set:

```
use function Gin0115\ElmishPHP\HTML\node;

echo node('custom-element', ['data-x' => 'y'])(text('whatever'));
// whatever
```

Type hierarchy
--------------

[](#type-hierarchy)

Everything renderable shares a small interface tree — useful for categorisation and type-narrowing in your own code:

```
Renderable extends \Stringable
├── Element
│   ├── BlockElement                                   div, p, h1-h6, ul, blockquote, ...
│   ├── InlineElement                                  span, a, strong, em, br, code, ...
│   ├── SectioningElement extends BlockElement         header, footer, nav, main, ...
│   ├── FormElement                                    form, input, button, select, ...
│   ├── TableElement                                   table, tr, td, th, ...
│   ├── MediaElement                                   img, video, iframe, ...
│   ├── InteractiveElement                             details, summary, dialog
│   └── VoidElement                                    marker on br, hr, img, input, ...
└── TextNode                                           Text, Raw

```

Every element function returns its concrete typed class (e.g. `div(...)(...)` returns `Gin0115\ElmishPHP\HTML\Element\Div`), so you can `instanceof BlockElement` or pass them around with full type info.

Supported tags
--------------

[](#supported-tags)

77 standard HTML elements out of the box.

CategoryTagsBlock`div`, `p`, `h1`–`h6`, `pre`, `blockquote`, `ul`, `ol`, `li`, `dl`, `dt`, `dd`, `figure`, `figcaption`, `hr`Inline`span`, `a`, `strong`, `em`, `small`, `b`, `i`, `u`, `mark`, `code`, `kbd`, `samp`, `sub`, `sup`, `time`, `abbr`, `cite`, `q`, `br`, `wbr`Sectioning`header`, `footer`, `main`, `nav`, `section`, `article`, `aside`Form`form`, `fieldset`, `legend`, `label`, `button`, `select`, `optgroup`, `option`, `textarea`, `input`Table`table`, `caption`, `colgroup`, `thead`, `tbody`, `tfoot`, `tr`, `td`, `th`, `col`Media`img`, `iframe`, `video`, `audio`, `canvas`, `picture`, `source`, `track`Interactive`details`, `summary`, `dialog`For anything else, use `node('tag-name', ...)`.

Tests
-----

[](#tests)

**PHPUnit** (unit / behaviour):

```
composer test
```

**Playwright** (browser-driven E2E against the kitchen-sink fixtures):

```
npm install
npm run server:up        # docker container on http://localhost:57893
npm run test:e2e --      # extra playwright flags after --
npm run server:down      # when finished
```

The kitchen-sink fixture at `tests/e2e/views/kitchen-sink.php` exercises every category — visit `http://localhost:57893/?fixture=kitchen-sink` while the server is up.

Requirements
------------

[](#requirements)

- PHP 8.2+
- (optional) Docker + Node for the e2e suite

License
-------

[](#license)

GPL-2.0-or-later

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance90

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity64

Established project with proven stability

 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

46d ago

### Community

Maintainers

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

---

Top Contributors

[![gin0115](https://avatars.githubusercontent.com/u/28779094?v=4)](https://github.com/gin0115 "gin0115 (15 commits)")

---

Tags

htmlfunctionalfpelm

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/gin0115-elmishphp-html/health.svg)

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

###  Alternatives

[phpoffice/phpword

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

7.6k37.4M218](/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.0M744](/packages/latte-latte)[twig/string-extra

A Twig extension for Symfony String

22349.2M200](/packages/twig-string-extra)[exercise/htmlpurifier-bundle

HTMLPurifier integration for your Symfony project

27611.8M19](/packages/exercise-htmlpurifier-bundle)[twig/markdown-extra

A Twig extension for Markdown

12115.5M114](/packages/twig-markdown-extra)[laminas/laminas-view

Fast and type safe HTML templating library with a flexible plugin system supporting multistep template composition

7527.7M258](/packages/laminas-laminas-view)

PHPackages © 2026

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