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

Laravel Html Generator Laravel Package

hnhdigital-os/laravel-html-generator

A Laravel package that generates HTML programmatically, offering a fluent builder API for creating tags, forms, and reusable components. Build clean, consistent markup directly in PHP instead of cluttering Blade templates, with chainable methods for attributes, content, and nesting. Useful for dynam...

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin, single-purpose package: a facade that builds HTML tag strings through a fluent API. It is not a form builder, not a Blade replacement, and not a component library — the entire value proposition is "stop hand-concatenating <div class="..."> inside PHP."

composer require hnhdigital-os/laravel-html-generator

Laravel 5.5+ should auto-register the service provider and the facade via package discovery; verify by checking the extra.laravel block in vendor/hnhdigital-os/laravel-html-generator/composer.json. If you are on an older skeleton, add the provider to config/app.php manually and alias the facade.

Where to look first, in this order:

  1. vendor/hnhdigital-os/laravel-html-generator/src/ — the facade accessor and the tag builder class. This is a small package; reading the two or three core files takes ten minutes and is more reliable than any docs you will find (there are effectively none).
  2. The service provider's register()/boot() — confirms whether the builder is a singleton, whether config is merged, and whether any extension hooks (Macroable) are wired.
  3. composer.json — the require block tells you the supported Laravel/Illuminate versions, which matters given the last release was November 2023.

First use case: the place this earns its keep is a Blade partial that is 90% markup with a handful of dynamic attributes — a badge, an alert, a card wrapper, a status pill. Instead of @include('partials.badge', ['type' => 'success']) for a one-line element, you render inline:

{!! Html::tag('span', ['class' => "badge badge-{$type}"], e($label)) !!}

Start there. If that feels cleaner than the partial you were about to write, keep going. If it feels like a lateral move, stop — the package does not scale into a bigger idea.

Implementation Patterns

Fluent attribute chaining. The natural workflow is build-then-render: create a tag, attach attributes, attach children, cast to string. In practice you will want a small private helper method in your controller or view composer rather than sprinkling calls across templates.

Building from collections. The strongest pattern is mapping over data you already have, which is where string concatenation gets ugly:

$items = $links->map(fn ($link) => Html::tag(
    'li',
    ['class' => $link->active ? 'is-active' : null],
    Html::tag('a', ['href' => $link->url], e($link->label))
))->implode('');

return Html::tag('ul', ['class' => 'nav'], $items);

Note the null attribute value — most builders in this family drop null/false attributes rather than emitting class="null". Confirm that behaviour in the source before you rely on it, because it is the single most common source of silent bugs in packages like this.

Rendering via __toString() / ->render(). Because the builder almost certainly implements __toString(), you can drop it into {!! !!} in Blade, into ->with() on a Mailable, or concatenate into a longer string. For Mailables and Notifications specifically this is a good fit: you often need one or two dynamic HTML fragments in a plain-text-adjacent context where creating a full Blade view is overhead.

Conditional attributes. Use the ternary-with-null idiom above rather than string-building the class list by hand. If the package exposes a when()/whenFilled() helper, prefer it; if not, a local helper function in app/Support/ that returns ['class' => $condition ? 'x' : null] keeps templates readable.

Testing. Since output is a deterministic string, assertions are trivial — no DOM parsing needed for simple cases:

$this->assertStringContainsString('href="/foo"', (string) Html::tag('a', ['href' => '/foo'], 'Foo'));

For anything with nested children, prefer assertSee/assertSeeHtml from a feature test against a real route so you are validating the rendered page, not the builder in isolation.

Integration tip: keep the builder out of your domain and view-model layers. It returns HTML strings; treat it as presentation-only and never let a class return Html output that another layer needs to inspect. If you need structured data about an element later, you have lost it — a string is the terminal format.

Gotchas and Tips

The package is effectively unmaintained. Two stars, zero dependents, one release in late 2023. That is not a reason to avoid it for a small internal tool, but it is a hard stop for anything long-lived or client-facing without a mitigation plan. Pin the exact version in composer.json, and accept that a future Laravel major will break it. The codebase is small enough that if it does break, forking it into app/Support/ is a couple of hours of work rather than a rewrite.

Escaping is your responsibility, not the builder's. A generator like this emits exactly the content you pass. If you pass $user->name directly as child content without e(), you have an XSS hole. The {!! !!} in Blade removes the framework's automatic escaping — you are trading Blade's safety net for terseness. Every dynamic text node needs e(); every dynamic attribute value needs e() too.

Compare against the incumbent before committing. spatie/laravel-html occupies the same niche with a vastly larger install base, real documentation, and an active maintainer. It has a comparable Html::element()/Html::div() style API. If you are choosing a dependency today, choose that one — the only reason to pick this package is that you inherited it.

this package spatie/laravel-html
Docs none extensive
Maintenance last release 2023 active
Ecosystem / examples ~0 large
Effort to swap — low, similar API shape

Do not mix it with Blade components for the same element. If resources/views/components/badge.blade.php exists, use it. Having two ways to render a badge means designers edit one and developers edit the other, and they drift. Pick builder-or-component per element type and write it down.

Attribute edge cases to verify in source: boolean attributes (disabled, checked) — does passing true emit disabled or disabled="1"? Void elements (<img>, <br>) — does the builder know to self-close, or will you emit <img></img>? Attribute ordering is not guaranteed and will make snapshot tests brittle; assert on substrings or parse the output rather than comparing whole strings.

Config caching. If the package merges a config file, php artisan config:cache will freeze it — a classic source of "works locally, broken in production." After any config change in a cached environment, re-cache. If you do not need the config, do not publish it.

Debugging is straightforward: dd((string) $tag) at any point in the chain shows exactly what will be emitted, because nothing is deferred. There is no deferred-render or queue behaviour to reason about, which is genuinely one of the package's better properties — the output is a pure function of the input.

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.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox