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 Tag Helper Laravel Package

beyondcode/laravel-tag-helper

Laravel Tag Helper by Beyondcode makes it easy to generate HTML tags programmatically in Laravel. Build elements and attributes with a simple API, avoid messy string concatenation, and keep Blade templates cleaner. Ideal for reusable components and dynamic markup, it is lightweight and framework-fri...

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with Composer: composer require beyondcode/laravel-tag-helper. The package targets the Laravel 5.5 auto-discovery era, so it should register BeyondCode\TagHelper\TagHelperServiceProvider automatically. If you are on anything older, add the provider to config/app.php manually. If Composer refuses the install on a current Laravel/PHP constraint set, stop — do not paper over it with --ignore-platform-reqs in a production lockfile.

  • Read the source before the README. The repo is archived (last release 2018-10-08), the documentation contradicts the changelog, and there are zero dependents to sanity-check against. The only trustworthy spec is the code. Open, in this order:

    1. composer.json — autoload.files (global helper file), declared illuminate/support constraint, PHP constraint.
    2. src/helpers.php (or wherever autoload.files points) — this is the entire public entry point. Note every declared function name verbatim.
    3. The Tag/TagHelper class — attribute methods, content methods, escaping behavior, void-tag handling.
  • Reconcile the scope contradiction first. The description advertises HTML tag helpers; the 1.1.0 release notes advertise auth, guest, and if helpers. Grep before you write a line:

    grep -RnE "function (tag|tag_|auth|guest|if)\b" vendor/beyondcode/laravel-tag-helper/src/
    

    If auth() is declared as a global function, you have a hard collision with Laravel's own auth() helper and the package is effectively unusable as-shipped. If if exists at all it cannot be a plain global function (if is a PHP reserved word) — find out how it is actually exposed before assuming anything.

  • Check the collision surface explicitly. In a scratch branch, dump the declared functions before and after autoloading:

    $before = get_defined_functions()['user'];
    // boot with the package installed
    dd(array_diff(get_defined_functions()['user'], $before));
    

    Anything unexpected in that diff is a CI/static-analysis hazard.

  • Minimal first use case — a link in Blade whose href, class, and disabled-style state come from view data. Instead of string concatenation:

    {{-- before: unreadable once conditionals pile up --}}
    <a href="{{ route('users.show', $user) }}"
       class="btn {{ $user->isActive() ? 'btn-primary' : 'btn-secondary' }}"
       @if($disabled) aria-disabled="true" @endif>
        {{ $user->name }}
    </a>
    

    you get a fluent builder whose conditionals vanish when false:

    {{ tag('a')
         ->attribute('href', route('users.show', $user))
         ->class(['btn', $user->isActive() ? 'btn-primary' : 'btn-secondary'])
         ->attribute('aria-disabled', $disabled ? 'true' : null)
         ->content(e($user->name)) }}
    

    Confirm the exact method names (attribute, class, id, content) against the source — the archived docs are not reliable on signatures.

  • Before you go further, do the side-by-side: modern Blade gives you @class([...]), @checked, @selected, @disabled, anonymous components, and Illuminate\Support\HtmlString. If those cover the use case, prefer them and treat this package purely as a pattern reference.

Implementation Patterns

Fluent builder inside Blade. The canonical usage is a single expression per element, rendered with {{ }}. Keep attribute calls on separate lines for reviewability; the whole point is that the markup shape is legible without @if sprinkling:

{{ tag('input')
     ->attribute('type', 'text')
     ->attribute('name', "address[{$index}][line1]")
     ->attribute('value', old("address.{$index}.line1"))
     ->attribute('required', $required)
     ->selfClosing() }}

Conditional attributes as the primary workflow. The high-value pattern is attribute($name, $value) where a null/false/empty value suppresses the attribute entirely. This is what replaces:

<option value="{{ $id }}" @if($selected) selected @endif>{{ $label }}</option>

with something re-evaluated from state, not from template branches. Validate the suppression semantics in the source — whether it is null, false, or both — and write a rendering test that pins the behavior, because this is the one behavior the whole ergonomic argument rests on.

Dynamic class and data-* from state. Build class lists from arrays or computed properties and let the helper flatten them; build data-* attributes from model/state arrays in one loop rather than one hardcoded attribute per line. Both of these are where hand-written Blade gets ugliest, so they are the best trial surface for a pilot.

Void and self-closing tags. <input>, <img>, <br>, <hr>, <meta>, <link> must not emit a closing tag. Find the exact mechanism (selfClosing() vs. an internal void-tag list) and test each one you use — emitting <input></input> will silently break some email clients and HTML validators even though browsers tolerate it.

Reusable fragments. Two viable integrations:

  1. Wrapper class — an app-owned App\Support\Ui\TableRow that internally uses the helper, so if you later drop the package you change one file.
  2. Anonymous Blade component — <x-form.input :name="$name" :required="$required" /> delegating to the helper, or replacing it entirely.

Either way, keep the helper calls behind a seam. Direct tag() calls scattered across forty views is the migration cost you are trying to avoid.

Mail templates. This is the strongest genuine use case: mail templates have no JavaScript escape hatch, so inline style attributes and conditional table markup are pure string assembly. A fluent builder with conditional attributes produces far more diffable templates. Test rendering by asserting on the compiled HTML string, not by eyeballing the inbox.

Nested content. Where the element wraps other markup, prefer a Blade slot/component over passing pre-rendered HTML into content(). Passing {!! ... !!} into the builder is the fastest way to lose escaping guarantees.

Testing pattern. Because this is a view-only utility, cover it with rendering tests rather than unit-testing internals:

$html = Blade::render('{{ tag("a")->attribute("href", "/x")->attribute("disabled", false)->content("Go") }}');
$this->assertStringContainsString('<a href="/x">Go</a>', $html);
$this->assertStringNotContainsString('disabled', $html);

Pin exactly the cases you depend on — attribute suppression, escaping of quote characters in attribute values, and void-tag output.

Gotchas and Tips

  • Archived means frozen. No security fixes, no CVE response path, no compatibility updates. Anything you build on it becomes internally owned by default. Prefer vendoring the MIT source into an app namespace (with attribution and a FROZEN — do not expect upstream updates note) over a Composer dependency, if you adopt it at all.
  • Global helper collisions are the top failure mode. A package-declared auth() will fatal at boot against Laravel's own helper, and if cannot be a normal global function. Diff get_defined_functions() in a scratch branch before integrating and again after any Laravel upgrade.
  • Escaping is not guaranteed just because it is "a helper." Check the source: does content() run e(), or does it accept raw HTML? Does it escape attribute values (quotes, <, &)? Double-escaping (&amp;amp;) and non-escaping (XSS via a dynamic href) are both plausible failure modes. Never route user-controlled values into attribute calls without confirming the escaping path, and never pass {!! !!}-rendered output into content().
  • Never let the auth-adjacent helpers make a decision. If auth/guest/if exist and evaluate guard state, they are presentation sugar at best. Keep @auth, @guest, @can, and policies/gates as the only authorization mechanism, and note in code review that a helper is not a gate. Their semantics are undocumented and unmaintained — an inverted check is a silent access-control defect.
  • Version compatibility is unverified, not merely old. A 2018 helper predates Laravel 6+ and PHP 8 entirely. Run composer why-not beyondcode/laravel-tag-helper against your target constraint, and confirm behavior on your actual Laravel/PHP pair in a throwaway branch. There is no compatibility matrix to consult.
  • Stale caches after any change. View and compiled-Blade caches will mask edits to helper output. When debugging "my attribute didn't render," run php artisan view:clear and php artisan config:clear before touching code.
  • Void tags and empty content. An element with zero content and an element that is self-closing render differently; confirm you are using the right one. This bites hardest in mail templates and in anything consumed by an HTML-to-PDF or HTML-to-text pipeline.
  • Attribute suppression semantics. Verify precisely what disappears: null, false, '', 0. Some values you want to render (value="0", data-count="0") are exactly the ones a naive falsiness check drops. Pin this with a test.
  • Extension points are effectively limited to the fluent builder's own methods. There is no maintained macro or plugin surface you should rely on. If you need a new attribute behavior, fork it — do not subclass an archived class and hope.
  • Exit plan, decided up front. Because the surface is presentational, reversibility is near zero-cost: pick one low-risk view, add rendering tests around it, and know today what replaces it (@class/@checked/@selected/@disabled plus anonymous components). If you vendor, name an owner. If you find it already in production, inventory call sites, confirm no global-symbol collisions, add rendering tests, and put it on the debt backlog rather than standardizing on it.
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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource