Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Html Object Laravel Package

anahkiasen/html-object

HtmlObject brings object-oriented HTML generation to PHP and Laravel. Build tags, lists, tables, and forms through a fluent, chainable API instead of messy string concatenation. Each element is a dedicated class you can extend, keeping markup clean, reusable, and easy to maintain.

Deep Wiki
Context7

Getting Started

Install with Composer and confirm it loads on your PHP version before writing code against it:

composer require anahkiasen/html-object

The autoloaded namespace is HtmlObject\. Read these first, in order:

  1. src/HtmlObject/Element.php — the general-purpose <tag> builder you will use 90% of the time.
  2. src/HtmlObject/Tag.php — the base class holding render() / __toString().
  3. src/HtmlObject/Traits/ — HasAttributes, HasChildren, and HasContent define the magic-property API.
  4. src/HtmlObject/{Link,Image,Input}.php — thin typed subclasses with pre-wired attributes.
  5. The README and tests/ — the exact signatures matter (they are not uniform across classes).

First use case — replace string-concatenated markup in a controller, mailer, or view helper:

use HtmlObject\Element;

$card = new Element('div', null, ['class' => 'card']);
$card->nest('h2', 'Welcome');
$card->nest('p', 'Thanks for signing up.');

echo $card;
// <div class="card"><h2>Welcome</h2><p>Thanks for signing up.</p></div>

Sanity-check with (string) $card in a REPL. The object renders to a string, so it drops into anything that accepts a string — Blade, a mailer body, a JSON response field.

Implementation Patterns

Set attributes three ways, pick the right one. Magic properties are the ergonomic default, but they cannot express names with hyphens or colons, so fall back to setAttribute() or the constructor array:

$el->class = 'btn';                      // magic __set
$el->setAttribute('data-id', 42);        // dashed/namespaced attributes
$el = new Element('a', 'Go', ['href' => '/x', 'class' => 'btn']); // bulk

Build children with nest(), which returns the child. That return value is what makes fluent tree building readable — chain off the child, not the parent:

$ul = new Element('ul', null, ['class' => 'menu']);
foreach ($items as $item) {
    $ul->nest('li', $item)->nest('span', ' →');
}

Prefer the typed helpers over new Element() when the tag has required attributes. They save you from remembering attribute order and make intent obvious in reviews:

use HtmlObject\Link;
use HtmlObject\Image;
use HtmlObject\Input;

echo Link::create('/login', 'Sign in', ['class' => 'btn']);
echo Image::create('/logo.png', 'Acme', ['width' => 120]);
echo Input::create('email', 'email', null, ['required' => true]);

Wrap recurring markup in a component class. This is where the package earns its keep — you get one place to change a card/alert/badge and every call site updates:

class Alert extends \HtmlObject\Element
{
    public function __construct(string $message, string $type = 'info')
    {
        parent::__construct('div', $message, ['class' => "alert alert-{$type}"]);
    }
}

echo new Alert('Saved.', 'success');

Override render() only if you need custom void-element or self-closing behavior; otherwise let the base class do it.

Splice dynamic layout trees. Build the shell once, then append/prepend/remove named nodes:

$layout = new Element('div', null, ['class' => 'layout']);
$layout->nest('header');
$main = $layout->nest('main');
$main->append($widget);      // append an existing Element
$main->prepend($banner);
$layout->removeChild(0);     // drop header

Assert on getChildren() / hasChildren() in tests rather than squinting at markup.

Render at the boundary, cache the string. Element objects are mutable; if you stash the object and mutate it later, every consumer sees the new state. Pass rendered strings between layers and keep the tree local to one method.

Blade integration is just echo. Because __toString() exists, {!! $card !!} works. If you want a terser syntax, write a helper:

function el(...$args): \HtmlObject\Element { return new \HtmlObject\Element(...$args); }

Gotchas and Tips

  • Last release was 2017-05-30. Treat this as legacy code: run the suite on your PHP version before committing. Expect PHP 8.2+ dynamic-property deprecations and possibly implicit-nullable warnings; either patch or pin a tolerant PHP version.
  • Escaping is on you. Do not assume content or attribute values are sanitized. Prove it with a <script>alert(1)</script> payload, then escape user input (htmlspecialchars($s, ENT_QUOTES, 'UTF-8'), or e() in Laravel) before handing it to the builder.
  • Magic attributes can't express data-* or aria-*. $el->dataFoo = 1 is fine; $el->data-foo will not parse. Use setAttribute('data-foo', 1) or the constructor array.
  • Don't nest into void elements. Image, Input, br, hr, meta have no children. nest() there produces invalid markup or silently drops it.
  • nest() returns the child, not the parent. The most common bug is chaining a second call expecting the parent. Keep explicit references when a chain gets longer than two calls.
  • Signatures differ per helper. Link::create($url, $content, $attributes), Image::create($src, $alt, $attributes), Input::create($type, $name, $value, $attributes) — don't guess, read the source; they are not normalized.
  • Empty vs. null content can render differently (<div></div> vs. a self-closed form) depending on the version. Pin your expected output with (string) assertions in tests.
  • Debugging levers: (string) $el, getAttributes(), getChildren(). If the markup looks wrong, dump the tree before blaming the renderer.
  • No meaningful configuration. If you recall a Laravel facade or service provider, verify it is actually registered in your app; otherwise just instantiate directly or bind it as a singleton.
  • Performance: each node is an object. For large tables or hundreds of <li>s built in a loop, plain string concatenation is faster — use this package where you actually manipulate the tree, not as a rendering engine for bulk output.
  • Weigh alternatives before a long-term bet: spatie/laravel-html, Blade partials, or raw DomDocument (for parsing/manipulating existing HTML). This package fits best when you want small, typed, nestable element builders and are comfortable maintaining a dormant dependency.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib