glhd/aire-bootstrap
Bootstrap 5 theme for Aire forms. Renders glhd/aire form components with Bootstrap-compatible markup, classes, and validation styles, so your Laravel forms match your Bootstrap UI out of the box—no rewriting form definitions.
glhd/aire-bootstrap is a theme package: it does not add new PHP APIs. It ships Bootstrap-flavoured Blade views for Aire's component namespace, so all the day-to-day work is done through Aire's form builder — this package only changes what gets rendered in the browser.
Minimum viable setup:
composer require glhd/aire glhd/aire-bootstrap
php artisan vendor:publish --tag=aire-bootstrap
You now have copies under resources/views/vendor/aire-bootstrap/. Read input.blade.php, select.blade.php, checkbox.blade.php, and _help.blade.php first — those are the files you will actually open when something looks wrong.config/aire.php (publish with php artisan vendor:publish --tag=aire-config if you have not already), set the component namespace to the Bootstrap theme's views — typically the components map or the top-level theme key, depending on your Aire version:
'components' => [
'root' => 'aire-bootstrap::form',
'input' => 'aire-bootstrap::input',
// ...
],
Check the theme's README or its source AireBootstrapServiceProvider — the exact key is the one thing that has shifted between Aire releases, so verify it against the version you installed rather than trusting a blog post.php artisan view:clear && php artisan config:clear.First use case: a login form. In a controller (or a form class) call Aire::open()->post(route('login')), add Aire::email('email') and Aire::password('password'), close with Aire::submit('Sign in'), then echo {!! $form !!} (or @aire) in Blade. If it renders with Bootstrap form-control, form-label, and invalid-feedback classes, the theme is wired correctly. If it renders bare HTML, the component namespace is still pointing at Aire's default (Tailwind-ish) theme.
Build forms through Aire, not through this package. Every day-to-day call is Aire::text(), Aire::select(), Aire::checkbox(), Aire::radio(), Aire::textarea(), Aire::submit(). The Bootstrap theme supplies the surrounding markup (labels, form-group/mb-3, help text, is-invalid + invalid-feedback, btn btn-primary). Do not hand-write Bootstrap markup alongside the builder — mix the two and styles will drift the moment you tweak the theme.
Prefer form classes for anything non-trivial. Aire supports class-based forms (php artisan make:aire-form in newer versions, or a plain class implementing toHtml()). Class-based forms:
{!! $form !!}).render(), and a queued job that emails a PDF of the form.->rules() and ->authorize() so validation errors land in the right bag before Aire renders.Use ->help() and validation messages on the field, not in the Blade view. This is the biggest ergonomic win of an Aire theme — help text and error markup are rendered by the theme and stay consistent:
Aire::email('email', 'Email address')
->help('We will never share this.')
->required();
When the form fails validation, Aire automatically reads $errors->get('email') and adds is-invalid + invalid-feedback. If you see no error styling, the error bag mismatch (see Gotchas) is almost always the cause.
Override specific components, not the whole theme. Once you have published the theme, copy one view out (e.g. input.blade.php), put it in resources/views/vendor/aire-bootstrap/input.blade.php with the same relative path, and edit that. Aire resolves the published view before the package view, so you get a surgical override without forking. Do not edit vendor/glhd/aire-bootstrap/ — a composer update will wipe it and you will be debugging a phantom.
Wire into Livewire with care. If you are using Aire inside a Livewire component, use wire:model on the field (Aire passes attributes through) and let Aire handle the label/help markup. Livewire's DOM diffing will preserve the theme's class names as long as you bind the model rather than toggling the field with Blade @if. If you need to re-render error state, $this->validate() before render() so Aire sees the errors during the same pass.
Leverage the theme for layout classes via Aire's field options. Rather than wrapping fields in <div class="col-md-6"> by hand, pass the Bootstrap column class through Aire's group or wrapper option where available:
Aire::text('first_name')->groupClass('col-md-6');
(The exact option name varies by Aire version — check Glhd\Aire\Elements\Group or run ->dd() on a field in Tinker to enumerate them.) This keeps responsive grids declarative and consistent, which is where a theme actually earns its keep.
Test the rendered HTML, not the package. In feature tests, hit the route that renders the form and assert on Bootstrap markers — assertSee('form-control', false), assertSee('invalid-feedback', false) after a failed submission. This catches the "someone changed the theme and forgot to republish" failure that unit tests miss, and it is the realistic coverage boundary for a third-party rendering package.
Bootstrap version drift is the main risk. This package was last released in April 2023 and is small. If you are on Bootstrap 5, expect to override a handful of views: form-group was removed in BS5, form-row became row g-*, and custom-control custom-checkbox became form-check. Grep the published views for custom- and form-group to find everything that needs patching before you trust it on a BS5 project. Bootstrap 4 is the safer target.
The package is 6 stars. Treat it as "a set of Blade templates I happen to install via Composer" rather than a maintained dependency. Budget half a day to read every published view after install; you are effectively forking the theme on day one. Do not adopt it if your team cannot own Bootstrap-specific Blade templates in-house.
Republishing overwrites your edits. php artisan vendor:publish --tag=aire-bootstrap --force will silently replace customised views. Never put --force in a deploy script. If you need to re-pull the upstream views for comparison, publish to a scratch namespace or diff against vendor/glhd/aire-bootstrap/resources/views/.
Error bag mismatches. Aire looks for errors under a specific bag (usually the default). If you are naming bags (->validateWithBag('login')), the field's ->errorBag() must match or the is-invalid class never fires. Symptom: the error message renders but the input does not highlight, or vice versa. Fix by setting the bag on the form class rather than per-field.
View cache is the #1 "it worked yesterday" bug. After editing a published view, php artisan view:clear. In production deploys, run view:cache as part of the build — but only after the publish step, or your overrides will render stale.
JS is your responsibility. Bootstrap's interactive components (modals, dropdowns, collapse, tooltips) need the Bootstrap JS bundle. The theme ships markup and aria attributes, not the script tag. If a checkbox renders but the tooltip on its ->help() never appears, this is why.
old() and Aire defaults interact. Passing ->value() on a field does not override a flashed old value — Aire prefers old(). When building "edit" forms, either let Aire handle it (default) or explicitly ->value($model->field) and accept that failed-validation state will still show the user's input. Test both flows: initial GET from a model, and POST-redirect-back with errors.
Livewire wire:model vs Aire ->value(). Do not set both. If you bind wire:model, let Livewire own the value and do not pass ->value() — otherwise the input flickers between server state and client state on each wire:model.defer round-trip. Same rule for wire:model.live.
Extension points to prefer. If you need behaviour the theme does not provide (a "floating label" wrapper, a signature pad field, a searchable select), add an Aire macro or register a custom component on the form class before you fork the theme's Blade views. Macros survive Aire upgrades; forked views do not.
How can I help you explore Laravel packages today?