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

Livewire Form Laravel Package

hungnm28/livewire-form

Reusable Blade UI components for Laravel and Livewire apps, including form inputs, buttons, modals, asides, theme switching, and Tailwind/Alpine-powered interactions. Config, views, and assets are publishable for easy integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites before you type a single command. This package is not self-contained — it assumes four things are already true about your app:

php -v                      # 8.3+
composer show laravel/framework livewire/livewire
  1. Tailwind CSS is compiled in the host app (not the CDN script tag).
  2. Alpine.js is available — Livewire 3/4 ships it, so you're fine unless you've overridden Livewire's frontend assets. Check config/livewire.php for inject_assets and your app.js.
  3. Tabler Icons are reachable (defaults to CDN via config assets.use_cdn_icons).
  4. Your app defines primary-* Tailwind colors.

Install:

composer require hungnm28/livewire-form

Auto-discovery registers anonymous Blade components under lf::. No provider registration required.

Wire up Tailwind first — this is step one, not an afterthought. If you skip it, every component renders unstyled and you'll waste an hour thinking the install failed:

// tailwind.config.js
content: [
  './resources/**/*.blade.php',
  './resources/**/*.js',
  './vendor/hungnm28/livewire-form/resources/views/**/*.blade.php',
],

Pick your primary palette. Either set theme => 'orange' in the published config, or map primary onto an existing palette in tailwind.config.js:

colors: { primary: colors.indigo }

First use case: a settings form inside a Livewire component. It exercises the form wrapper, wire:model binding, a select, a toggle, and a submit button without touching modals or JS:

{{-- resources/views/livewire/settings-form.blade.php --}}
<x-lf::form wire:submit="save">
    <x-lf::form.input wire:model="form.name" label="Name" />
    <x-lf::form.input wire:model="form.email" label="Email" />
    <x-lf::form.select wire:model="form.role" label="Role"
        :data="['admin' => 'Admin', 'editor' => 'Editor']" />
    <x-lf::form.toggle wire:model="form.active" label="Active" value="1" />

    <x-lf::btn.primary type="submit">Save</x-lf::btn.primary>
</x-lf::form>

Where to look first when something is wrong: the published views. Run php artisan vendor:publish --tag=livewire-form-views early — even if you don't intend to customize — so you have the actual Blade source to read instead of guessing at props.

Only import the JS if you need it. Form/table/button components are pure Tailwind + Blade. The JS bundle only powers asides, theme cycling, fullscreen, and the themeSetting store:

// resources/js/app.js — only if you use aside/theme/fullscreen
import './vendor/livewire-form/app';

Implementation Patterns

Wrap the vendor components in your own app-level components. This is the single highest-leverage pattern for a package with zero dependents and no changelog. Define <x-ui.input> as a thin wrapper around <x-lf::form.input> with your defaults baked in. When the upstream component signature shifts, you patch one file instead of 60 views.

Modal-driven edit flows are the most idiomatic CRUD pattern here. The modal binds to a public boolean, and you drive it through Livewire's normal lifecycle:

<x-lf::modal wire:model="editing" title="Edit user" size="lg">
    <x-lf::form wire:submit="update">
        <x-lf::form.input wire:model="form.name" label="Name" />
        <x-lf::btn.primary type="submit">Update</x-lf::btn.primary>
    </x-lf::form>
</x-lf::modal>

From Alpine or plain JS, bypass Livewire entirely:

window.dispatchEvent(new CustomEvent('open-modal', { detail: 'modal-id' }));

Give the modal an explicit id when you're opening it from JS — detail is the modal's identifier, not the Livewire property name.

Compose tables from the primitives, don't expect a data-table component. The package gives you structure (table, table.thead, table.tr, table.th, table.td, table.actions) and presentational props (sortable, direction, align, density, strong, muted, nowrap). The tools slot is your toolbar. Sorting is visual only — you wire wire:click and an orderBy property yourself:

<x-lf::table.th sortable direction="{{ $sort === 'name' ? $direction : 'asc' }}"
    wire:click="sortBy('name')">Name</x-lf::table.th>

Establish one layout shell and never repeat it:

{{-- resources/views/components/layouts/admin.blade.php --}}
<x-lf::layout title="{{ $title ?? 'Admin' }}">
    <x-slot:aside>
        <x-lf::aside>
            <x-lf::aside.item title="Dashboard" icon="home" url="/admin" />
        </x-lf::aside>
    </x-slot:aside>
    <x-lf::navbar title="{{ $title ?? 'Dashboard' }}" />
    <main class="p-4">{{ $slot }}</main>
</x-lf::layout>

Register package Livewire components through config rather than guessing at class names, keeping the alias convention consistent with Blade:

'livewire' => [
    'components' => [
        'lf-user-form' => \Hungnm28\LivewireForm\Livewire\UserForm::class,
    ],
],

Theme switching uses the themeSetting Alpine store, so read and write theme state through the store rather than toggling classes yourself — <x-lf::theme.setting /> and the cycleTheme action both assume it exists.

Gotchas and Tips

The Tailwind content path is the #1 source of "works locally, broken in production." If you publish views, the published copies live outside vendor/ — add that path to content too, or you'll generate classes for the vendor views and not the ones actually rendering.

primary-* utilities are not shipped. The components reference classes like bg-primary-600; if your config doesn't define primary, those classes silently don't exist and buttons render transparent. Symptom: components render structurally but look washed out.

Publishing views means you own the diff forever. Publish the config always (cheap), publish views only when you actually need to change markup, and commit them so upgrades are a readable git diff instead of a mystery. There is no changelog beyond "Version 2.1.5" — the diff is the changelog.

Pin the version. Zero stars, zero dependents, no meaningful release notes. Use an exact constraint ("hungnm28/livewire-form": "2.1.5") and upgrade deliberately.

Config changes need php artisan config:clear. Especially the livewire.components map — with a cached config, new aliases simply won't resolve and you'll get a "component not found" that looks like a typo.

Don't let Alpine load twice. If your app entrypoint imports Alpine explicitly and the package JS runs, you can get duplicate store registration for themeSetting. Either rely on Livewire's Alpine or bundle both deliberately — never both by accident. The same trap applies to use_cdn_icons: set it to false the moment your app bundles @tabler/icons-webfont, or you're double-fetching a webfont.

wire:model in Livewire 3 is deferred by default. With wire:model.live you get per-keystroke validation; with plain wire:model the error state on <x-lf::form.input> won't update until the next request. Verify how the input renders validation errors before you build a form-heavy screen — check the published view for @error / $errors->has() handling. If it's absent, standardize on a wrapper that adds it:

<x-lf::form.input wire:model="form.email" label="Email" />
@error('form.email') <p class="text-sm text-red-600">{{ $message }}</p> @enderror

Use the corrected alias, not the typo. form.toogle and theme.toogle exist for backwards compatibility only. Grep your codebase for toogle and fix it now — the aliases are the first thing to be dropped in a major bump.

Attribute passthrough is your cheapest extension point. Before publishing views, try class="..." and standard HTML attributes on the component — anonymous Blade components typically merge them. Reach for publishing views only when markup structure, not styling, needs to change.

Debugging checklist when a component misbehaves: php artisan view:clear, confirm the vendor content path in tailwind.config.js, confirm primary is defined, read the source in vendor/hungnm28/livewire-form/resources/views/, then check whether Alpine is actually mounted (window.Alpine in console). Roughly 90% of issues land in the first three.

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.
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
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle