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

orchestra/html

Orchestra/Html is an HTML and form builder for Laravel, offering a fluent API to generate markup, forms, and Bootstrap-friendly components. It extends Laravel's HTML helpers with reusable macros, cleaner syntax, and convenient builders for rapid view development.

View on GitHub
Deep Wiki
Context7

Getting Started

orchestra/html is a thin Orchestra-flavoured layer over Laravel Collective's HTML/Form builders. It adds control() (label + input + wrapper markup) and Orchestra-specific conventions, then binds form and html into the container. Day-to-day it is a form-scaffolding accelerator for admin and extension screens — not a general-purpose view layer.

Step 1 — confirm you actually have it (don't require it blind). On Orchestra Platform it normally arrives transitively via orchestra/foundation:

composer show orchestra/html
php artisan package:discover --ansi

Step 2 — confirm what is behind the facades. This single check determines whether you're using Orchestra's builder or Collective's:

dd(get_class(app('form')), get_class(app('html')));

Orchestra\Html\FormBuilder / Orchestra\Html\HtmlBuilder means you're wired up. Collective\Html\... means the Orchestra provider didn't register or was overridden — that's a registration bug, not a rendering bug, and you should stop and fix it before writing markup.

Where to look, in order of payoff:

  1. composer.json — the illuminate/* and collective/html constraints. This is the real gate on whether you can install at all; read it before anything else.
  2. src/HtmlServiceProvider.php — the container keys, facade aliases, and whether macros/Blade directives are registered in boot().
  3. src/FormBuilder.php / src/HtmlBuilder.php — ignore everything inherited from the Collective parent; every method not inherited (control(), raw(), etc.) is where the actual value lives.
  4. resources/views/ — any shipped partials you can @include instead of hand-rolling.

First use case — one create/edit screen, end to end:

{!! Form::open(['route' => 'orchestra::users.store']) !!}
    {!! Form::control('name',  'Name',  Form::text('name',  null, ['class' => 'form-control'])) !!}
    {!! Form::control('email', 'Email', Form::email('email', null, ['class' => 'form-control'])) !!}
    {!! Form::submit('Save', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}

Render it, confirm a single _token is present, confirm labels/inputs land where expected. That one screen is your go/no-go signal — validate it before refactoring any other view or writing adapters.

Implementation Patterns

Make Form::control() your default field wrapper. It exists to bundle label + input + error block so you aren't repeating markup per view. The pattern that holds: build the inner input first, then pass the resulting string into control() with field name and human label. Attributes belong on the inner call. Putting class on control() is the classic mistake that yields a styled label next to an unstyled input.

{!! Form::control(
    'email',
    'Email address',
    Form::email('email', null, ['class' => 'form-control', 'required' => true])
) !!}

Post to named routes, never literal URLs. Orchestra extensions live behind a platform prefix and can be remounted; /admin/users breaks the moment the prefix changes. Form::open(['route' => 'orchestra::users.store']) survives it. Use ['action' => ...] only for genuinely dynamic endpoints that cannot be named.

Use Form::model() on every edit screen. This is the highest-leverage feature and the one worth standardising on:

{!! Form::model($user, [
    'route' => ['orchestra::users.update', $user->id],
    'method' => 'PUT',
]) !!}
    {!! Form::control('name', 'Name', Form::text('name', null, ['class' => 'form-control'])) !!}
{!! Form::close() !!}

You get value population, old-input repopulation after a failed validation redirect, and method spoofing for free. If you're writing value="{{ old('name', $user->name) }}" by hand, you're fighting the builder — pass null as the value and let the model binding do it.

Register macros in boot(), and prefix them with your own namespace. Macros are the extension point the package actually gives you, and they're globally scoped for the whole app:

Form::macro('adminText', function ($name, $label, array $options = []) {
    return $this->control(
        $name,
        $label,
        $this->text($name, null, array_merge(['class' => 'form-control'], $options))
    );
});

Prefix (adminText, houseSelect) so you can't collide with Orchestra, Collective, or a future package. Keep macro bodies to markup composition only — no business logic.

Put an adapter between the package and your domain code. Given the maintenance signals (0 dependents, a release that only bumps orchestra/support), do not let Form:: calls spread through views you'll keep long-term. A thin factory gives you a swap point:

class FieldFactory
{
    public function text(string $name, string $label, array $attrs = []): string
    {
        return Form::control($name, $label, Form::text($name, null, $attrs));
    }
}

Views call $fields->text(...) or a Blade component; replacing the implementation later is one file, not a codebase-wide find-and-replace.

Scope it to admin CRUD by default. The realistic day-to-day win is the fifth nearly-identical create/edit form in an internal panel. Reach for it there. Don't reach for it in public-facing views, Livewire components, or a codebase that already has a Blade component design system — you'd be introducing a second, conflicting convention.

Test the rendered string, not the browser. Form builders fail subtly, and string assertions catch it cheaply:

$this->get(route('orchestra::users.edit', $user))
    ->assertSee('name="name"', false)
    ->assertSee('value="'.e($user->name).'"', false)
    ->assertSee('name="_token"', false);

In at least one test, assert substr_count($response->getContent(), 'name="_token"') === 1 — duplicate CSRF tokens silently break POSTs and are trivially caught by a count.

Gotchas and Tips

The illuminate/* constraint is the real gate. If Composer refuses installation against your Laravel version, take the no-go and use native Blade components or Laravel Collective. Do not fork to widen the constraint unless an existing Orchestra extension hard-depends on it — you'd be maintaining a package upstream isn't actively developing.

Facade alias collision is the most likely day-one surprise. Both this package and Collective alias Form and Html. Registration order changes, or a legacy app with Collective also installed, can leave the wrong builder behind the facade — methods that "exist" but behave differently. Re-run get_class(app('form')) whenever behaviour looks off.

Double-escaping is the number one rendering bug. Form::control() returns markup, so it must be echoed raw: {!! Form::control(...) !!}. {{ Form::control(...) }} prints visible angle brackets. Conversely, Form::raw() and Html::raw() bypass escaping entirely — audit every use and treat them as review-required. The defaults are sensible, but only hold if you don't neutralise them.

control() does not reliably wire validation state. Expect to add has-error / is-invalid yourself based on $errors->has($name). Submit a failing form early and look at the output, or you'll ship admin forms with invisible errors.

Unclosed forms bleed model state. Form::model() sets internal state for subsequent field calls. A missing Form::close() — easy when a partial is included mid-form — means the next form on the page inherits the previous model's values. If a later form has mysteriously prefilled fields, look for the missing close() in an @include above it.

Duplicate _token inputs cause 419s. Happens when Form::open() sits inside Form::model(), or a partial reopens a form. A PUT/PATCH/DELETE open also emits _method — don't add one yourself in the layout.

Provider ordering matters for macros and Blade directives. A macro that's "undefined" in one environment but fine in another is almost always registration order in config/app.php / bootstrap/providers.php. Macros registered in boot() run per-request; anything assuming they exist at config time (e.g. inside another provider's register()) will fail.

Asset publishing is a quiet 404 factory. Orchestra extensions often need a vendor:publish step. If package-shipped styles or scripts 404 in staging, check the publish step before the browser cache.

Bootstrap-3-era markup — budget for the CSS. control()-style wrappers were written for older Bootstrap form markup. On Bootstrap 5 or Tailwind, expect to override classes via the attributes array or wrap the output. Fine for admin screens, a hard stop for a design-system-critical surface.

Pin versions and watch dependency drift. With a thin release cadence, an accidental minor bump of a transitive Orchestra package is a likelier surprise than an upstream breaking change. A committed composer.lock plus a documented upgrade procedure is the mitigation.

Debug rendering with dd, not the browser. dd(Form::control(...)) shows the exact string before Blade touches it, which instantly distinguishes "builder emitted wrong markup" from "Blade escaped it." Saves twenty minutes every time.

Write the exit plan before the fifth form. The fallback is simple — native Blade components — and it stays simple only if the surface stays small: a handful of prefixed macros, an adapter, and zero Orchestra\Html type hints in domain code. If you find yourself extending Orchestra\Html\FormBuilder, you've coupled too far; add an adapter instead.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource