tomsix/laravel-components-library
A Laravel library of reusable Blade components for building consistent interfaces faster, with ready-made UI elements, form controls, and layout helpers that keep your views clean and your design system easy to maintain.
Require the package (v2.3.5 is the first release declaring Laravel 13.x support, so pin ^2.3.5 if you're on Laravel 13):
composer require tomsix/laravel-components-library:^2.3.5
Read the source before the README. For a component library, the authoritative docs are the component classes and Blade views themselves. Start in vendor/tomsix/laravel-components-library/:
src/ — component classes, service provider, any config.resources/views/ — the actual markup and the slot/prop names you'll be passing.config/ — anything publishable.Publish what's publishable, once:
php artisan vendor:publish --provider="TomSix\ComponentsLibrary\ComponentsLibraryServiceProvider"
Check the command output — publishing config is safe; publishing views means you now own them and stop receiving upstream fixes.
Smoke-test one component in isolation before wiring it into a page. Create a throwaway Blade view that renders the simplest component with the minimum required props, load it in the browser, and confirm the markup and any bundled CSS/JS resolve. If styles are missing, you have an asset-publishing or @vite/@stack wiring problem — not a component problem — and it's cheaper to find that now.
First real use case: take one repeated chunk of markup already in your app (a card, a table row, an alert/badge, a form input group) and replace it with the library's equivalent in a single view. Diff the rendered HTML against what you had. That one swap tells you whether the library's conventions match yours before you commit to a wider migration.
Wrap, don't scatter. Don't call vendor components directly from dozens of views. Introduce a thin app-level layer — either your own Blade components that delegate, or a small set of partials:
{{-- resources/views/components/ui/card.blade.php --}}
<x-vendor-card {{ $attributes->merge(['class' => 'app-card']) }}>
<x-slot:header>{{ $title }}</x-slot:header>
{{ $slot }}
</x-vendor-card>
This gives you one place to change when a component's API shifts in a minor release, and one place to enforce your design tokens instead of passing the same classes on every call site.
Attribute forwarding is the integration point. Most component libraries accept $attributes and merge them onto a root element. Lean on that for class, id, wire:model, x-on:*, and data-* rather than expecting props for every case:
<x-vendor-input name="email" :error="$errors->first('email')" class="w-full" wire:model.live="email" />
If a component doesn't forward attributes, that's a strong signal to wrap it rather than to edit vendor views.
Livewire / Inertia interplay. Component libraries are usually plain Blade, so they compose fine with Livewire as long as attribute forwarding works and you avoid passing closures or non-serialisable props across the wire. For Inertia, you're typically rendering the library's markup inside your own page components — verify that any bundled JS is idempotent, because Inertia navigations don't do full page reloads and component scripts that only run on DOMContentLoaded will silently stop working after the first navigation.
Layout/nav components get set up once, in your layout file. Anything that's a shell (nav, sidebar, layout wrapper) belongs in resources/views/layouts/app.blade.php and nowhere else. Resist per-page usage — duplicated shells diverge fast.
Form components: centralise error rendering. Decide once whether the component handles $errors display or whether your wrapper does. Mixing the two produces duplicate error messages that are annoying to track down.
Version-constrain deliberately. With a patch release bumping framework compatibility, keep "tomsix/laravel-components-library": "^2.3" in composer.json and let your lockfile pin the exact patch. Add a CI job that runs composer update --prefer-lowest on your supported floor so you learn about version drift before production does.
Laravel 13 compatibility landed in a patch release — treat that as a signal. The jump from v2.3.4 to v2.3.5 is a composer.json constraint widening (illuminate/support and friends). Practical implications:
^2.3.4 may resolve but isn't declared compatible. Require at least ^2.3.5.2.3.4 exactly, you'll need to bump. Check composer why-not tomsix/laravel-components-library 2.3.5 if the update is blocked.Pin-and-test the upgrade path. In a patch release like this, the realistic failure mode isn't the library — it's Laravel 13's own deprecations surfacing in the views you've published or overridden. Diff your published views against the vendor originals after every upgrade:
diff -r vendor/tomsix/laravel-components-library/resources/views resources/views/vendor/components-library
Published views are a fork. The moment you publish and edit, upstream fixes stop reaching you. Prefer wrapping over publishing. If you must publish, leave a // published from v2.3.5 comment at the top of each file so you know how stale it is.
Debugging a component that renders nothing or wrong markup: dump the resolved component instead of guessing.
php artisan view:clear
then in a Tinker session or a route closure, Blade::render('<x-vendor-card title="x" />') — this surfaces "unable to locate component" and "too few arguments" errors immediately, without a browser round-trip. For prop-name mistakes, remember Blade resolves kebab-case attributes to camelCase constructor params (:user-id → $userId); silent nulls are almost always that.
Service provider registration is auto-discovered, but if you've disabled package discovery, register TomSix\ComponentsLibrary\ComponentsLibraryServiceProvider manually — otherwise every <x-...> tag from this package fails with a confusing "component not found".
Asset caching bites. After publishing CSS/JS, php artisan view:clear && php artisan cache:clear plus a hard browser refresh. Component libraries frequently ship styles via a @once block or a stack push, and a stale compiled view means you'll debug markup that the browser isn't even receiving.
Extension points, in order of preference: (1) attribute merging on your own wrapper, (2) Blade::componentNamespace() or anonymous component overrides in resources/views/components/, (3) publishing and editing views. Escalate only as far as you need to — each step trades upgradeability for control.
Escape hatch planning. Because the release cadence here looks low-traffic (patch-only, single-contributor changelog), keep your wrapper layer thin enough that replacing the underlying library is a day's work, not a quarter's. That means: no vendor types in your own component signatures, no reliance on internal (non-published) view paths in tests, and assertions against rendered output behaviour rather than exact HTML.
How can I help you explore Laravel packages today?