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

arcanedev/laravel-html

Arcanedev/Laravel HTML provides HTML and form builders for Laravel, letting you generate forms, inputs, links, and other markup with a fluent PHP API. It simplifies Blade templates, reduces repetitive HTML, and supports Laravel conventions.

View on GitHub
Deep Wiki
Context7

Getting Started

arcanedev/laravel-html restores the classic Laravel 4 Html / Form facades — fluent builders that emit escaped-ready markup from PHP instead of hand-written Blade. It is a maintained-lineage fork of laravelcollective/html with the same API surface, so anything you remember from that package transfers directly.

Install and publish

composer require arcanedev/laravel-html
php artisan vendor:publish --provider="Arcanedev\LaravelHtml\LaravelHtmlServiceProvider"

The service provider is auto-discovered on Laravel 5.5+, and it registers the Html and Form aliases for you — do not add them to config/app.php yourself. The published config/laravel-html.php controls the framework flavour used for component classes and which icon set the helpers assume; for a plain Tailwind/Bootstrap app set it once and forget it.

Where to look first

  • Arcanedev\LaravelHtml\HtmlBuilder — script, style, image, link, linkRoute, mailto, ul/ol, attributes, tag, entities/decode.
  • Arcanedev\LaravelHtml\FormBuilder — open/close, model, and every field type plus label, submit, token, selectRange/selectYear/selectMonth.
  • The Contracts directory — bind these if you want constructor injection inside your own services or test doubles.

First use case — a create/edit form that does the boring parts for you

{{-- resources/views/posts/edit.blade.php --}}
{!! Form::model($post, ['route' => ['posts.update', $post], 'method' => 'PATCH', 'files' => true]) !!}
    <div class="field">
        {!! Form::label('title', 'Title') !!}
        {!! Form::text('title', null, ['class' => 'input', 'required' => true]) !!}
        @error('title') <p class="error">{{ $message }}</p> @enderror
    </div>

    <div class="field">
        {!! Form::label('body', 'Body') !!}
        {!! Form::textarea('body', null, ['rows' => 6, 'class' => 'input']) !!}
    </div>

    {!! Form::select('status', ['draft' => 'Draft', 'published' => 'Published'], null, ['class' => 'input']) !!}

    {!! Form::submit('Save', ['class' => 'btn']) !!}
{!! Form::close() !!}

That single call gives you: the _token hidden field, the _method=PATCH spoof, enctype="multipart/form-data", prefilled values from $post, and — after a failed validation redirect — the flashed old input overriding the model values. That old-input-first resolution order is the entire value proposition of the package.

Implementation Patterns

Model binding as the default for edit screens. Form::model($entity, $options) sets an internal model; every subsequent field resolves its value as old input → model attribute → explicit argument. Always pair it with Form::close() — that is what emits the _method override, not Form::open(). For the create screen, use Form::open(['route' => 'posts.store']) and pass explicit values only where you need a default.

Route/action targeting. Prefer 'route' => ['posts.update', $post] over url() strings or action() arrays; it keeps the form correct under locale prefixes, subdomains, and signed routes. Form::open(['route' => 'x', 'secure' => true]) forces HTTPS for the action URL.

Macros: turn the builder into your design system. This is the pattern that keeps the package from becoming a maintenance burden.

// AppServiceProvider::boot()
Form::macro('textField', function ($name, $label, $value = null, array $attrs = []) {
    return new HtmlString(
        Form::label($name, $label)
        . Form::text($name, $value, array_merge(['class' => 'input'], $attrs))
    );
});

Html::macro() works the same way for non-form components. Macros are per-process, so re-register inside tests (setUp) or use the container binding instead if you need them in queue workers.

Array-notation fields. Names like items[0][qty] and tags[] are supported, and FormBuilder::transformKey() rewrites ., [ and ] to _ for the id/for pair — so Form::label('items[0][qty]') and Form::text('items[0][qty]') stay correctly associated. Build repeated rows with a @foreach over old input (old('items', $defaults)) so a failed save doesn't lose rows.

Lists and assets. HTML::ul($collection, ['class' => 'list']) renders nested arrays recursively; HTML::script('js/app.js', ['defer']), HTML::style(...), and HTML::image('img/logo.png', 'Logo', ['class' => 'h-8']) keep asset markup consistent. HTML::attributes($array) is a useful standalone serialiser for your own custom components — it flattens array values with spaces and drops null/false booleans.

Composition with validation and Blade. The builder is orthogonal to @error / $errors; render messages yourself next to each field. For value display (non-input) use {{ $post->body }}; the Form builders are only for controls. If you build a SPA/Inertia front end, the package becomes largely dead weight — you'll only use Html::attributes().

Gotchas and Tips

Version compatibility is the real risk. Last tagged release is Nov 2020, targeting Laravel 8-era APIs. There is no Laravel 9/10/11 support, and Laravel's Str/HtmlString refactors plus the removal of legacy container aliasing break it. Pin with an explicit constraint, and treat adoption as a short-to-medium-term decision: plan a migration to Blade components (<x-form.input>) or spatie/laravel-html before your next major Laravel upgrade. With 6 stars and 0 dependents, expect no upstream fixes.

Alias collisions. Html and Form are global aliases. If laravelcollective/html or any package registering the same names is present, resolution becomes order-dependent and you'll get "method not found" at random. Check php artisan about / config/app.php for duplicates.

Escaping and HtmlString. Field values are HTML-escaped by the builder; that's correct behaviour. If you need pre-rendered markup inside a label, wrap it in HTML::decode() explicitly. Whether methods return HtmlString (safe under {{ }}) or plain strings (needs {!! !!}) varies by release — dd(Form::text('x')) once, then standardise on one syntax across the codebase rather than mixing.

Checkboxes need a hidden zero. There is no implicit companion input, so an unchecked box posts nothing and $request->boolean('agree') can't distinguish "absent" from "false". Always emit Form::hidden('agree', 0) immediately before Form::checkbox('agree', 1). Same reasoning for multi-selects returning [].

files => true is not automatic. Form::file() will silently produce a broken upload without enctype="multipart/form-data" on the open()/model() call. This is the single most common bug report.

CSRF is version-sensitive. Some builds inject _token inside Form::open()/Form::model(), others require an explicit Form::token(). Verify in the rendered source once; don't rely on memory across upgrades. Excluded URIs in VerifyCsrfToken still apply.

Select/radio "selected" is old-input-first. After a failed save, old() wins over the bound model — that's what you want, but it means the explicit third argument to Form::select() is not authoritative. If you need a hard override, bypass the helper or clear old input.

Numeric-string option keys. PHP casts "0", "1" array keys to integers and "" to 0. A select built from a collection of IDs with a 0 option can render the wrong option as selected. Cast explicitly or build the option list with string-prefixed keys.

Macro return type. A macro that returns null or a non-string produces mangled attribute output mid-tag. Always return new HtmlString(...) or a string.

Debugging. Because everything funnels through HtmlBuilder::attributes(), adding Form::macro('dump', fn() => new HtmlString('<pre>'.e(json_encode(Form::getModel() ...)).'</pre>')) isn't available — instead dd(Form::getSessionKey()), session()->getOldInput(), and the bound model to diagnose wrong prefill. Clear php artisan config:cache and view:cache after editing the published config; stale config is a frequent phantom-bug source.

Extension points, in order of preference: macros → container binding of FormBuilderContract/HtmlBuilderContract with your own subclass → publishing and overriding the compiled views (there are none, so subclassing is the only real route). Prefer the first; subclassing ties you to internal method signatures that will not be maintained.

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