nexus633/laravel-bootstrap-ui
Laravel Bootstrap UI provides ready-made Blade components for building Bootstrap-styled interfaces in Laravel apps. Speed up development with reusable, consistent UI elements and less repetitive markup.
This release (1.1.0) turns the package from a pure Bootstrap component library into one with an opinionated app-shell feature: a searchable command palette with both backend and Blade registration. If you are installing fresh, start there — it is the fastest way to see the whole stack (Blade component → service → facade → JS) working.
1. Install and publish
composer require <vendor>/<package>
php artisan vendor:publish --tag=<package>-config # check `php artisan vendor:publish` for the exact tags
Look first at three places in the package source, in this order:
src/Providers/*ServiceProvider.php — where components are registered (Blade::componentNamespace), where the CommandPalette and BootstrapUi facades are bound, and whether any JS/CSS is pushed to a stack or published to public/.resources/views/components/ — the actual Blade files. This is the real documentation for props/slots; the README lags behind on prop names.config/ — anything toggleable (default shortcut, palette defaults, prefix for component names).2. Drop the palette into your layout
{{-- resources/views/layouts/app.blade.php --}}
<body>
{{ $slot }}
<x-bs::command-palette />
</body>
3. Register your first commands from the backend
// app/Providers/AppServiceProvider.php
use <Vendor>\<Package>\Facades\CommandPalette;
public function boot(): void
{
CommandPalette::register('Create invoice', route('invoices.create'))
->keywords(['new', 'billing']);
CommandPalette::register('Log out', route('logout'), method: 'POST');
}
First real use case: replace the scattered "quick actions" dropdown in your admin header. Backend-register everything that maps to a route or a named action (global, works on every page), and use declarative Blade items only for page-local commands.
If you are upgrading from 1.0.x, do the breaking-change sweep before anything else — see Gotchas and Tips — because nav and tab/* no longer exist and your views will throw Unable to locate a class or view for component [bs::nav] at render time, not at build time.
Keep palette registration out of AppServiceProvider once you have more than a handful of commands. A CommandPaletteServiceProvider (or a Palette bootstrapper class) keeps it greppable and lets you wrap registration in an if (app()->isProduction()) / permission check:
CommandPalette::register('Invite user', route('users.invite'))
->visible(fn () => auth()->user()->can('invite-users'));
Treat visible/authorization callbacks as mandatory for anything that isn't purely navigational — a command palette is a global, always-mounted surface, and a client-side filter over the item list is not access control.
<x-bs::command-palette.item> is the right tool when the command only makes sense on the current page (a "Export this report" action). Put them inside the page view, not the layout:
<x-bs::command-palette>
<x-bs::command-palette.item
label="Export this report"
:action="route('reports.export', $report)"
/>
<x-bs::command-palette.item label="Duplicate" wire:click="duplicate" />
</x-bs::command-palette>
The trade-off: declarative items only exist on pages that render them, so anything a user expects to find "everywhere" (dashboard, settings, logout) must go through the backend facade.
BootstrapUi tooThe new BootstrapUi facade is the most under-hyped part of this release. Internally it builds class strings, styles and attributes; use it in your own custom components instead of hand-concatenating Bootstrap classes. That is the extension point that makes your in-house components survive the next theme/purge change:
use <Vendor>\<Package>\Facades\BootstrapUi;
$classes = BootstrapUi::classes('btn', ['btn-primary' => $primary, 'btn-sm' => $small]);
If you maintain an internal component library layered on top of this package, migrate your own components to BootstrapUi before the next minor release — the 1.1.0 refactor signals the package is now opinionated about class generation, and hand-rolled strings are exactly what will drift.
nav → tabs<x-bs::nav> and its children are gone. Anything that was a tab strip becomes <x-bs::tabs>:
{{-- 1.0.x --}}
<x-bs::nav>
<x-bs::nav.link :href="route('profile')" active>Profile</x-bs::nav.link>
<x-bs::nav.link :href="route('security')">Security</x-bs::nav.link>
</x-bs::nav>
{{-- 1.1.x --}}
<x-bs::tabs>
{{-- check resources/views/components/tabs/ for the exact child API,
it does not map 1:1 to the old nav.link / tab.pane pair --}}
</x-bs::tabs>
Do not assume the new API is a rename. The release notes describe tabs as "more flexible", which usually means the children changed shape (tabs declared as a collection rather than as pre-rendered panes). Read the component source, and migrate one page first before a global find-and-replace.
The palette's value in an SPA-ish setup is that navigation commands should behave like links (full page visit) while actions dispatch events or call $wire methods. Keep two registration helpers in your app — registerNavigation() and registerAction() — so the two kinds never get mixed up, and so you can later swap one to wire:navigate without touching every call site.
Removed: nav, nav/dropdown, nav/dropdown-item, nav/link, tab/content, tab/pane, partials/button-content. Note that the package refers to components both with slashes and dots, so search both forms:
grep -rnE '<x-bs::(nav|tab)[./]|<x-bs::partials[./]button-content' resources/
Every hit is a broken page. There is no deprecation shim, so pin ^1.0 in composer.json on branches you are not ready to migrate, and do this sweep as the first commit of the upgrade branch — not the last.
If you ever ran vendor:publish --tag=<package>-views, you now have stale copies of the old components in resources/views/vendor/. Those override the package's new ones, so the BootstrapUi refactor and the tabs component simply will not take effect — and you will debug the wrong layer. Check resources/views/vendor/ before anything else when a component "still looks like 1.0". Re-publish with --force only after diffing your customisations.
Ctrl+KCtrl+K is the default, and it collides in three common ways: browser/OS shortcuts (Firefox and some Linux DEs), editor-like bindings your users expect (Slack/GitHub-style search), and other in-app libraries. Two mitigations: make the shortcut configurable via the config file rather than hardcoding, and make sure the handler ignores keypresses while focus is inside input, textarea, select or a contenteditable — otherwise typing k after a Ctrl chord in a form opens the palette. Test on macOS (Cmd) and Windows/Linux separately; a German-locale release note means the maintainers are testing on non-US keyboards too, but verify.
The palette is mounted in your layout, so its item list ships on every request. Two failure modes:
If you have a very large command set, defer it: register a small static set and fetch the rest on open, rather than shipping hundreds of items on every render.
The release notes describe the palette (opening on Ctrl+K, being searchable) but say nothing about assets. Before you conclude it is broken, confirm how the package delivers its JavaScript: check the service provider for Blade::directive, @push to a stack, or a published asset, and confirm you have included it in your layout (and that Vite is building it if it is a published module). Symptom of a missing asset: the component markup renders but Ctrl+K does nothing — which looks identical to a shortcut collision.
partials/button-content removal has a wider blast radius than it looksThat partial was a shared internal used to standardise button internals (icons + text + spinner). If you referenced it directly in your own components, the replacement is the <x-bs::text> / <x-bs::icon> pairing that is now standardised across the package. If you did not reference it directly, you still might be affected indirectly if you extended package button components with custom slots — re-check buttons with badges, icons and loading states after upgrading.
Unable to locate a class or view for component [bs::nav] → you missed the sweep, or a cached compiled view: php artisan view:clear.tabs renders empty → children API changed; inspect resources/views/components/tabs/.visible callback returns false.BootstrapUi::classes().php artisan view:clear && php artisan optimize:clear. Blade component caches are the most common source of "the upgrade did nothing".BootstrapUi facade — build your own class/style/attribute strings so custom components track package conventions.CommandPalette facade — the single place to add, group, keyword-tag and authorise commands; wrap it in your own thin service so a future API change is a one-file edit.How can I help you explore Laravel packages today?