berry/html
Build HTML templates directly in PHP with a fluent, type-safe element builder. Compose tags, attributes, classes, and children, then render to string. Great for minimal templating and dynamic UI patterns (e.g., HTMX) without context switching.
Installation
composer require berry/html
Requires PHP 8.3+.
First Use Case Create a simple HTML element chain:
use Berry\Html\Element;
echo div()->text('Hello, Berry!')->toString();
Outputs:
<div>Hello, Berry!</div>
Where to Look First
Berry\Html\Element, Berry\Html\Tag, and Berry\Html\Rel (for link relations).counterButton and layout functions demonstrate common patterns.div()->attr()) or generate PHPDoc-based docs with phpDocumentor.Workflow: Build components as reusable functions returning Element objects.
function card(string $title, string $content): Element {
return div()
->class('card')
->child(header()->child(h2()->text($title)))
->child(div()->child(p()->text($content)));
}
Integration Tip: Use dependency injection (e.g., Laravel’s service container) to pass shared elements (e.g., headers/footers).
Pattern: Leverage PHP logic to conditionally add attributes or children.
function userCard(array $user): Element {
return div()
->class('user-card ' . ($user['active'] ? 'active' : ''))
->attr('data-id', $user['id'])
->child(avatar()->src($user['avatar'] ?? null));
}
Tip: Use ->when(bool $condition, callable $callback) for complex logic:
div()->when($user['admin'], fn($el) => $el->class('admin'));
Example: Combine with HTMX for dynamic updates (as in the README):
button()
->attr('hx-post', '/update')
->attr('hx-swap', 'outerHTML')
->text('Update');
Tip: Pair with berry/htmx for type-safe HTMX helpers.
Blade-Like Templates: Replace Blade with Berry in views:
// routes/web.php
Route::get('/dashboard', fn() => view('dashboard', ['user' => $user]));
// resources/views/dashboard.php
echo layout(
main()->child(userProfile($user))
);
Tip: Use Laravel’s View::make() to render Berry components:
return View::make('component', ['data' => $data])->render();
Pattern: Nest components hierarchically.
function page(Element $content): Element {
return html()
->child(head()->child(title()->text('My Page')))
->child(body()->child($content));
}
echo page(
div()->child(h1()->text('Welcome'))
);
Strict Typing:
text() or attr() may throw TypeError if arguments don’t match expected types (e.g., passing null to text()).assert() or is_string() checks:
$title = $user['title'] ?? 'Untitled';
assert(is_string($title));
h1()->text($title);
Attribute Ordering:
Html::attributes()). If order matters (e.g., for CSS specificity), manually chain attributes:
div()->class('base')->class('modifier'); // Order preserved
Self-Closing Tags:
<img> or <input> require explicit selfClosing():
img()->src('/image.jpg')->selfClosing();
Inspect Elements:
Use ->toString() to debug rendered output:
echo div()->child(p()->text('Debug'))->toString();
PHPStan: The package includes PHPStan rules. Run:
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse src
HTMX Debugging:
For HTMX issues, inspect the hx-* attributes and ensure the server responds with valid HTML fragments.
Custom Tags:
Extend Berry\Html\Tag to add domain-specific tags:
class MyTag extends Tag {
public static function create(): self { return new self('my-tag'); }
}
// Usage: MyTag::create()->attr('data-x', '123');
Global Modifiers:
Create a base Element wrapper to add shared attributes:
function baseElement(Element $el): Element {
return $el->attr('data-app', 'berry');
}
// Usage: baseElement(div()->text('Hello'));
Laravel Service Provider:
Bind Berry’s Element factory to Laravel’s container:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->bind(Element::class, fn() => div());
}
Html facade by aliasing:
use Berry\Html\Element as BerryElement;
How can I help you explore Laravel packages today?