Skip to content

Basic Usage ​

Prerequisites: Quick Start; On this page: the tag API — creating elements, setting attributes, immediate rendering.

The examples assume the application has already loaded Composer's vendor/autoload.php; import the specific Pure\HTML, Pure\SVG, or Pure\Core symbols used by the snippet.

Tag API vs. compiled rendering

This page documents the tag API used for snippets, prototypes, and debugging; trees built this way render immediately via render() / print(). Production pages should compile shapes instead — see Compiled Rendering.

Basic Syntax ​

1. Creating HTML Elements ​

PurePHP provides two ways to create HTML elements:

PurePHP uses function calls to create HTML elements:

php
<?php

use function Pure\HTML\div;
use function Pure\HTML\h1;
use function Pure\HTML\p;

// Create a simple div element
div('Hello World')->print();

// Create nested elements
div(
    h1('Title'),
    p('Paragraph content')
)->class('container')->print();

Magic Static Methods (For custom tags) ​

php
<?php

use Pure\Core\HTML;

// Create custom HTML elements using magic methods
// (the method name is the tag name: this renders <customTag class="custom">)
HTML::customTag('Custom content')->class('custom')->print();

// Perfect for web components or non-standard tags
HTML::myComponent(
    HTML::header('Component Header'),
    HTML::content('Component Body')
)->data_component('my-component')->print();

Custom tags take the same children arguments as the functions, so a dynamic tag name works too:

php
<?php

use Pure\Core\HTML;

$tag = 'my-element';
HTML::{$tag}('Content')->class('dynamic')->print();

Use a fixed allowlist for dynamic tag names. The renderer writes the tag name as supplied; it does not make an untrusted name safe.

2. Setting Attributes ​

Use method chaining to set element attributes:

php
<?php

use function Pure\HTML\div;

div('Content')
    ->class('container')
    ->style('background: #fff;')
    ->data_key('primary')
    ->id('main')
    ->print();

Choosing the Right Approach ​

When to Use Each Method ​

  • Best for: Standard HTML tags, everyday development
  • Advantages: Clean syntax, good performance, excellent readability
  • Example: div(), p(), span(), etc.

Use Magic Static Methods ​

  • Best for: Custom tags, web components, dynamic tag names
  • Advantages: Works with any tag name, elegant syntax
  • Example: HTML::customElement(), HTML::webComponent()
php
<?php

use function Pure\HTML\div;
use Pure\Core\HTML;

// Function approach - standard tags
$standard = div('Standard content')->class('container');

// Magic method - custom tags
$custom = HTML::myCustomTag('Custom content')->data_component('special');

Important Usage Notes ​

1. String Content vs Raw Content ​

String children and ordinary attribute values are escaped as HTML syntax. This prevents a string from becoming a new tag or quote-breaking attribute, and keeps comparison text such as 2<3 or a<b visible. It is not a complete URL or script policy: href/src schemes, event-handler attributes, style, and dynamic tag names still need validation or a Content Security Policy.

php
<?php

use Pure\Core\Raw;

use function Pure\HTML\div;

// ✅ String content is escaped, not parsed
div('<p>This is shown as text</p>')->print();
// Output: <div>&lt;p&gt;This is shown as text&lt;/p&gt;</div>

// ✅ Use Raw::of to emit trusted markup
div(Raw::of('<p>This is preserved</p>'))->print();
// Output: <div><p>This is preserved</p></div>

Markup-looking strings are shown as text instead of being parsed. This boundary is syntactic: it does not validate a URL such as javascript: or an event attribute, and it cannot make Raw::of() safe.

Why this matters:

  • Syntax safety: Escaping prevents ordinary text and attributes from creating markup
  • No data loss: Text that merely looks like markup is kept visible
  • Intentionality: Emitting markup requires an explicit Raw::of() wrapper
  • Defense in depth: Validate URL schemes and active attributes, restrict dynamic tag names, and use CSP

When to use Raw::of():

  • Including pre-formatted HTML/XML content you control
  • Embedding a template or external document after validating its source
  • Working with trusted HTML/XML strings
  • Including JavaScript or CSS only when the document's CSP and trust model allow it

2. className Alias ​

Since class is a PHP keyword, PurePHP provides className as an alias:

php
<?php

use function Pure\HTML\div;

// Both ways work
div('Content')->class('container')->print();
div('Content')->className('container')->print();

3. Built-in Utility Functions ​

The class() method has a built-in clx function, and style() has a built-in sty function to handle arrays and conditional parameters:

php
<?php

use function Pure\HTML\div;

$isActive = true;
$isLarge = false;

div('Content')
    ->class('btn', $isActive ? 'active' : null, $isLarge ? 'large' : null)
    ->style(['color' => 'red', 'font-size' => '16px'])
    ->print();

When you need to merge class/style strings on their own, reach for clx() / sty() from Utility Functions.

4. Attribute Naming Rules ​

Since - has special meaning in PHP, attributes like data-id need to be written as data_id:

php
<?php

use function Pure\HTML\div;

div('Content')
    ->data_id('123')           // corresponds to data-id="123"
    ->data_type('card')        // corresponds to data-type="card"
    ->aria_label('Button')     // corresponds to aria-label="Button"
    ->print();

5. Adding Child Elements ​

You can add multiple child elements through parameters:

php
<?php

use function Pure\HTML\div;
use function Pure\HTML\p;

div(
    p('First paragraph'),
    p('Second paragraph'),
    p('Third paragraph')
)->class('content')->print();

Common HTML Tags ​

PurePHP supports all common HTML tags:

php
<?php

use function Pure\HTML\{
    div, span, p, h1, h2, h3, h4, h5, h6,
    a, img, ul, ol, li, table, tr, td, th,
    form, input, button, textarea, select, option
};

// Create a link
a('Click here')->href('https://example.com')->print();

// Create an image
img()->src('image.jpg')->alt('Image description')->print();

// Create a list
ul(
    li('Item 1'),
    li('Item 2'),
    li('Item 3')
)->class('list')->print();

// Create a form
form(
    input()->type('text')->name('username'),
    input()->type('password')->name('password'),
    button('Submit')->type('submit')
)->method('POST')->action('/login')->print();

SVG Support ​

PurePHP has built-in support for SVG tags:

php
<?php

use function Pure\SVG\{svg, circle, rect, path};

// Create a simple circle
svg(
    circle()
        ->cx('50')
        ->cy('50')
        ->r('40')
        ->stroke('black')
        ->stroke_width('3')
        ->fill('red')
)->width('100')->height('100')->print();

// Create a rectangle
svg(
    rect()
        ->x('10')
        ->y('10')
        ->width('80')
        ->height('80')
        ->fill('blue')
)->width('100')->height('100')->print();

SVG is treated as a fragment and does not add an xmlns attribute automatically. For a standalone .svg file, set the namespace explicitly with ->xmlns('http://www.w3.org/2000/svg'); save() still supplies the XML declaration unless you pass a custom header.

Conditional Rendering ​

Use PHP conditional statements for conditional rendering:

php
<?php

use function Pure\HTML\{div, p};

$isLoggedIn = true;

div(
    $isLoggedIn ? p('Welcome back!') : p('Please log in')
)->class('message')->print();

With compiled rendering, the condition becomes a Slot::if() placeholder and the branches are shapes. Slots such as Slot::value() stand in for the values that are bound at render time:

php
<?php

use Pure\Compile\Compile;
use Pure\Core\Slot;

use function Pure\HTML\{div, p};

function Message(bool $isLoggedIn): string
{
    static $render;
    $render ??= Compile::shape(
        div(
            Slot::if(
                'isLoggedIn',
                p('Welcome back!'),
                p('Please log in')
            )
        )->class('message')
    );

    return $render(['isLoggedIn' => $isLoggedIn]);
}

echo Message(true);

Slot::if() reads the current data scope, a missing key is false, and the shape is memoized in static so it is built once per process.

Loop Rendering ​

Use PHP loop statements to render lists:

php
<?php

use function Pure\HTML\{ul, li};

$items = ['Apple', 'Banana', 'Orange'];

ul(
    ...array_map(fn($item) => li($item), $items)
)->class('fruits')->print();

With compiled rendering, lists are Slot::each() slots: the item shape is rendered for every element of the bound iterable, with Slot::value() marking the value to bind:

php
<?php

use Pure\Compile\Compile;
use Pure\Core\Slot;

use function Pure\HTML\{ul, li};

function Fruits(array $items): string
{
    static $render;
    $render ??= Compile::shape(
        ul(Slot::each('items', li(Slot::value('name'))))->class('fruits')
    );

    return $render(['items' => $items]);
}

echo Fruits(['Apple', 'Banana', 'Orange']);
echo Fruits([
    ['name' => 'Apple'],
    ['name' => 'Banana'],
    ['name' => 'Orange'],
]);

An item is the scope its shape reads, so ['name' => 'Apple'] supplies that key. Because the shape renders one slot, an item may also be the value itself, which is why the first call above passes the strings as they are.

Style Handling ​

1. Inline Styles ​

php
<?php

use function Pure\HTML\div;

div('Content')
    ->style('
        background: #f0f0f0;
        padding: 20px;
        border-radius: 8px;
    ')
    ->print();

2. Class Name Handling ​

One class() call joins all of its arguments, and a second class() call replaces the value of the first, so pass every class in a single call:

php
<?php

use function Pure\HTML\div;

$isActive = true;

div('Content')
    ->class('container', $isActive ? 'active' : 'inactive')
    ->print();

Next Steps ​

Released under the MIT License