exolnet/laravel-bootstrap4-form
A Laravel package that brings Bootstrap 4 form styling to your views, offering reusable helpers and components for inputs, labels, validation errors, and buttons so you can build clean, responsive forms faster.
composer require exolnet/laravel-bootstrap4-form
Before anything else, check the constraint this release installs into your app:
composer show exolnet/laravel-bootstrap4-form
v2.5.0 drops Laravel 11 support and adjusts the PHP constraint (see Gotchas — this is the single most important fact about this release). Confirm the resolved illuminate/* and php requirements before writing code against it.
Where to look first:
vendor/exolnet/laravel-bootstrap4-form/composer.json — the authoritative support matrix.php artisan vendor:publish --tag=config
Form facade class and the tests/ directory — the tests are the fastest way to see the exact markup each method emits (classes, wrapper order, invalid-feedback placement).First use case — replace a hand-written Bootstrap 4 form group with the builder's output:
{{ Form::open(['route' => 'posts.store']) }}
{{ Form::text('title', null, ['label' => 'Title', 'help' => 'Keep it under 80 characters.']) }}
{{ Form::select('status', ['draft' => 'Draft', 'live' => 'Live']) }}
{{ Form::submit('Save', ['class' => 'btn btn-primary']) }}
{{ Form::close() }}
If that renders a form-group wrapper, a form-control input, a is-invalid state on validation failure, and repopulates from old input, you're wired correctly.
Model binding for edit forms. This is where the package earns its keep — values, selected options, and checked states come from the model, and old input automatically wins after a failed validation redirect:
{{ Form::model($post, ['route' => ['posts.update', $post], 'method' => 'PUT']) }}
{{ Form::text('title') }}
{{ Form::textarea('body') }}
{{ Form::checkbox('published') }}
{{ Form::close() }}
Pair with FormRequests, not inline validation. The builder reads from the default $errors bag and old(); a FormRequest redirect-back flows straight through with no extra glue.
Wrap it in your own Blade components. The builder is a low-level primitive. Centralize your design system once:
{{-- resources/views/components/field.blade.php --}}
<div class="form-group">
{{ Form::label($name, $label) }}
{{ Form::$type($name, $value, ['class' => 'form-control'.($errors->has($name) ? ' is-invalid' : '')]) }}
@error($name) <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
Then call <x-field name="title" label="Title" type="text" /> everywhere. This isolates you from package churn (see the L11 drop) and from any Bootstrap 4→5 migration later.
Custom fields via macros. When you need a repeating field type (money input, address block), register a macro in a service provider rather than duplicating markup:
Form::macro('money', function ($name, $value = null, $options = []) {
return Form::text($name, $value, array_merge(['class' => 'form-control', 'step' => '0.01'], $options));
});
AJAX/partial validation. When validation happens over JSON rather than a redirect, there is no $errors bag and no old input — pass values explicitly and apply is-invalid yourself. Don't expect the builder to know about your fetch response.
Testing. Assert on rendered markup (assertSee('is-invalid', false), assertSee('value="'.$post->title.'"', false)) and simulate old input with $this->withSession(['_old_input' => ['title' => 'typed']]) to cover repopulation.
composer update on an L11 project will silently resolve to 2.4.x (or fail if you've pinned ^2.5). If you're on L11, pin "exolnet/laravel-bootstrap4-form": "^2.4" and stay put until you've upgraded Laravel. If you're on L12, verify the PHP constraint matches your runtime (php -v) before deploying — the release adjusts it.composer.json. Floating constraints on packages that prune framework versions are how you get a production deploy resolving to a different major than staging.laravelcollective/html (or anything else) is installed, you'll have two Form aliases and one will win arbitrarily. Remove the old one; the API surface is similar enough that a mechanical swap is usually fine.Form::close(). Opening a model-bound form leaves the builder bound to that model; a subsequent Form::open() in the same view can inherit stale state and render wrong values.->withInput() on the validation failure is what populates _old_input; without it, repopulation looks broken and you'll blame the package.php artisan config:clear (or config:cache in deploy). Stale cached config is the #1 cause of "my class overrides aren't applying."form-group → spacing utilities and custom-control → form-check. Auditing every generated field is cheaper than a half-migrated stylesheet; the component wrapper above makes the swap a one-file change.tinker — Form::text('title', 'x') — and diff against the package's feature tests. That distinguishes "config not applied" from "validation state missing" in seconds.How can I help you explore Laravel packages today?