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

okipa/laravel-form-components

Laravel form components for Bootstrap 4/5 that generate HTML dynamically, are Livewire compatible, and stay fully customizable. Includes form, input, textarea, and more.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm your stack first. The install is a single command with no service provider registration, no migrations, and no mandatory config publish:

composer require okipa/laravel-form-components

The real work is confirming two gates before you write a line of Blade:

  1. CSS framework. Bootstrap 4 or Bootstrap 5 only. TailwindCSS 3 is listed as "upcoming" in the README — treat that as not shipped. If your app is Tailwind, stop here; there is no usable path today.
  2. Livewire version. The compatibility matrix pins Livewire ^2.0 across every release line including 1.3. If you're on Livewire 3, these components will not wire correctly.

Then check the matrix row for your Laravel/PHP pair and pin the exact version you land on in composer.json rather than leaving a caret range (see Gotchas).

Where to look first. The README shows the whole API surface in one snippet; the actual component-by-component reference lives in the wiki. Read the wiki page for x:form::input and x:form::form before anything else — those two carry the attribute conventions (:bind, caption, name, group, locales) that every other component inherits. If you're migrating from okipa/laravel-bootstrap-components, read the upgrade guide in docs/upgrade-guides/ first and skip the wiki for now — the call mapping is near 1:1.

First use case. Don't adopt this on a greenfield toy page. Take your ugliest existing CRUD form — the one with 15 hand-written <div class="form-group"> blocks, manual old() fallbacks, and a manual @error block under each field — and rewrite just that one view. That's where the value shows up immediately and the risk is contained:

<x:form::form method="PUT" :action="route('user.update', $user)" :bind="$user">
    <x:form::input name="name"/>
    <x:form::input type="email" name="email"/>
    <x:form::select name="role" :options="$roles"/>
    <x:form::button.submit/>
</x:form::form>

Note what disappeared: the @csrf, the @method('PUT'), the value="{{ old('name', $user->name) }}", the is-invalid class logic, and the error message markup. That's the actual product.

Implementation Patterns

:bind is the core workflow, and it's a create/edit switch. Pass a model and every child component hydrates its value from it; pass nothing and you're in create mode. The idiomatic pattern is one shared form partial for both:

{{-- resources/views/users/_form.blade.php --}}
<x:form::form :method="$user->exists ? 'PUT' : 'POST'"
              :action="$user->exists ? route('users.update', $user) : route('users.store')"
              :bind="$user">
    ...
</x:form::form>

Keep the :bind on the wrapper, not on individual inputs. Spreading it per-field defeats the point and invites inconsistency between fields.

Let the wrapper own the HTTP semantics. x:form::form emits CSRF, method spoofing, and the action. Don't hand-write <input type="hidden" name="_method"> alongside it — you'll get duplicates. Also verify the casing it expects for method against the wiki; the README uses PUT uppercase, and if the component does a strict match you want to be consistent project-wide from day one rather than discovering it form by form.

Livewire: keep wire:model and name together. Changelog 1.0.4 and 1.0.5 exist specifically because inputs weren't emitting a name attribute when they were wired. That's fixed, but the practical rule stands: always supply name explicitly even when using wire:model, don't rely on the component inferring it:

<x:form::input name="email" wire:model.defer="email"/>

For a Livewire screen, bind to the public property and let the component handle presentation; keep validation in the Livewire component's $rules and use wire:model for state. Don't try to use :bind with an Eloquent model and wire:model on the same field — pick one source of truth per screen.

Group components expect arrays. checkbox and radio take :group="[value => label]", and select takes :options="[value => label]". For multiple selects and checkbox groups, the bound model attribute must be castable to an array ($casts = ['hobbies' => 'array']) or you'll get a silent mismatch on save. This is the single most common integration bug and it's a model-layer fix, not a component fix.

Compose, don't configure. There is no config file worth fighting with. The extension mechanism is Blade itself: wrap clusters of components in your own anonymous component when a field pattern repeats.

{{-- resources/views/components/form/field-row.blade.php --}}
<div class="col-md-6">
    {{ $slot }}
</div>

Then <x-form.field-row><x:form::input name="name"/></x-form.field-row>. Layout ownership stays with you; the package owns the field internals.

Textarea locales. :locales="['fr', 'en']" renders a tabbed multi-language textarea. Use it only when your model actually has translated columns — otherwise it's an impressive feature that produces confusing output. Check the wiki for the expected model shape before wiring it into a multilingual form.

Buttons close the loop. x:form::button.submit and x:form::button.link handle the Bootstrap button classes and the link-to-cancel case. Use them so your action row matches the visual language of the fields — mixing hand-written <button class="btn btn-primary"> with the package components is where the styling drift starts.

Gotchas and Tips

Pin the exact version, not a caret range. Release 1.2.0 explicitly "locked PHP versions to 8.1 and 8.2." A package that constrains PHP minors will produce composer resolution failures at the worst possible moment — typically mid-composer update on a Friday. Pin "okipa/laravel-form-components": "1.3.x" (or the exact tag) and upgrade deliberately.

Livewire 3 is a hard stop, not a warning. Every release line in the matrix says ^2.0. If your roadmap includes a Livewire 3 upgrade, sequence it before adopting this package, or accept that adopting it now creates a future blocker you'll have to unpick. Don't assume "it's Blade components, it'll probably work" — wired inputs are exactly where Livewire version drift bites.

Bootstrap 4 carries the longest tail. Releases 1.0.1 through 1.0.9 are overwhelmingly BS4 fixes — toggle-switch classes, file input labels, validation class positioning, caption placement inside input groups. That means BS4 is well-trodden but also that its edge cases are where the bugs historically lived. If you're on BS4 and using file inputs, toggle switches, or input groups, check the rendered output carefully; if you're on BS5, you're on the less-bug-fixed path but also the one with fewer known landmines.

Validation errors need a live test, not an assumption. The components render is-invalid/is-valid and an error message slot, and the changelog shows error-message positioning has been fixed repeatedly around input groups. Write one feature test or just manually submit a failing form against a field wrapped in an input group with an addon, and confirm the message lands in the right place. This is the fastest way to find out whether your Bootstrap version's specifics are handled.

old() vs :bind precedence is worth confirming once. The expected behavior is old() wins after a failed validation, falling back to the bound model. Verify it with one deliberately broken submission rather than assuming — if the precedence is inverted in your version, your edit forms will silently discard user input on validation failure, which is a nasty bug to find later.

Debug by looking at the HTML, not the component. There's no debug mode or config toggle. When a field renders wrong, view:source the page and diff against what the Bootstrap docs say the markup should be. The package is a thin Blade wrapper; the bug is almost always a class placement issue or a missing attribute, both visible in source. php artisan view:clear after any vendor view changes.

Check what you can publish before you fork. If you need to change markup, confirm whether views are publishable (vendor:publish with the package's tag) — if they are, you customize in place and stay on upstream. If they aren't, you're overriding components by name in your own resources/views/components, which works but silently drifts from upstream. Either way, that decision determines your long-term maintenance cost far more than the initial install.

Fork plan before you ship. 19 stars, 0 dependents, single maintainer. This is not a reason to avoid the package — the scope is small and the code is Blade templates, so a fork is cheap. But name an owner and confirm the fork is actually viable (can you run its tests, can you publish the views) before it's on forty forms. Adoption risk is a decision to make explicitly, not a thing to discover during a Livewire upgrade.

Migrating from okipa/laravel-bootstrap-components is the easy win. There's a dedicated upgrade guide and near-1:1 call mapping. Scope the refactor to the views that actually use the predecessor, migrate one module, and confirm the rendered output matches — don't turn it into an open-ended rewrite of the whole view layer.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin