grizzlylab/ui-bundle
GrizzlyLab UI Bundle packages reusable interface components and templates for PHP applications, helping developers assemble consistent, maintainable front ends faster. It centralizes common UI patterns so teams can ship polished screens with less boilerplate.
First, the reality check that shapes everything else. grizzlylab/ui-bundle is a Symfony 2/3 Twig bundle, not a Laravel package. It has no ServiceProvider, no Facade, no Blade views, and it was last released in December 2016. composer require grizzlylab/ui-bundle in a Laravel app will install files you cannot use. The real day-to-day leverage is as a reference implementation: a small, readable set of Bootstrap markup partials you port into Blade components once and own forever.
Where to look first (in the repo):
Resources/views/ — this is the whole product. Alert and modal markup live here; read these before anything else.Twig/ — extension classes showing which functions/filters the templates expose (e.g. an alert() helper or a flash-rendering function). This tells you what the intended call site looks like.DependencyInjection/Configuration.php — the config tree, which reveals the knobs (Bootstrap version, dismissible flag, icon set, default types).Resources/config/ — service wiring, only relevant if you're auditing what gets injected.Minimal Symfony setup (for context, not for your Laravel app): register the bundle in AppKernel, import any bundle config, then {% include %} the alert partial from your base layout and set a flash message in a controller before redirecting.
Your actual first use case in Laravel: replace your hand-rolled flash block. Build <x-alert> and render it from the layout, driven by session('status'). That single component covers 80% of what this bundle does.
{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info', 'dismissible' => true])
<div {{ $attributes->merge(['class' => "alert alert-{$type}"]) }} role="alert">
{{ $slot }}
@if ($dismissible)
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
@endif
</div>
Flash-to-alert pipeline. The bundle's model is: controller sets a flash bag entry with a type (success, info, warning, danger), the base layout includes the alert partial, the partial iterates the bag and maps each type to a Bootstrap contextual class. Port that as:
@foreach (['success', 'error', 'warning', 'info'] as $key)
@if (session()->has($key))
<x-alert :type="$key === 'error' ? 'danger' : $key">{{ session($key) }}</x-alert>
@endif
@endforeach
Keep the key→class mapping in one place (a component default or a config array), never inline at call sites. The mapping is the thing that breaks when Bootstrap versions change.
Modal pattern. The bundle renders modals as hidden partials in the layout, triggered by data-toggle / data-target attributes. In Blade, model this as a component with named slots for title/body/footer:
<x-modal id="confirm-delete" title="Delete record?">
<x-slot:footer>
<button class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button class="btn btn-danger" wire:click="destroy">Delete</button>
</x-slot:footer>
This action cannot be undone.
</x-modal>
Pair it with a data-target="#confirm-delete" trigger. If you're on Livewire, prefer wire:click opening a single reusable modal over one modal per row — the per-row pattern is what the 2016 bundle encouraged and it does not scale.
Rendering strategy. Two valid choices, and the bundle only supports the first:
Integration tips. Keep partials dumb — no queries, no business logic in the template. Pass prepared data in. In Symfony this was a hard rule enforced by Twig's sandbox; in Blade it's a discipline you have to impose yourself, and it's the main reason a ported component stays maintainable.
It will not install cleanly on anything modern. Symfony 2/3 constraints, likely PHP ~5.5|~7.0 requirements, no Flex recipe. Don't fight the dependency solver — read the source on GitHub instead of installing it. Zero dependents and 2 stars mean no one is maintaining a fork.
Bootstrap 3 markup is baked in. Expect alert-danger, close (not btn-close), data-toggle/data-target, hidden, pull-right. Bootstrap 5 renamed all of these: text-bg-danger, btn-close, data-bs-toggle, d-none, float-end. Copy-paste from the templates will produce silently unstyled buttons — the markup renders, nothing happens, no console error.
Twig-to-Blade syntax gap. {% if %} → @if, {{ x }} stays, {% include 'X' %} → <x-x /> or @include, filters (|raw, |trans) → helpers or {!! !!}. Mechanically simple, but the bundle's template inheritance ({% extends %}, {% block %}) maps to slots, not to @section/@yield — do that translation deliberately.
|raw / {!! !!} on modal bodies is an XSS footgun. The 2016 bundle was written for a world where modal bodies were trusted. If you port it and feed model data into the body, escape by default.
Double-rendered flash messages. The classic port bug: the partial is included both in the base layout and in a page-level template, so users see each message twice. Include it once, in the layout, and test a redirect-to-page flow explicitly.
Dismissible alerts without JS. If Bootstrap JS isn't loaded on that page, the close button is inert. The data-dismiss attribute is a Bootstrap JS contract, not a browser one.
Debugging. In Symfony, bin/console debug:twig lists the registered extensions and their functions — that's how you discover what the bundle actually exposes. On the Laravel side, php artisan view:clear and php artisan about after touching components; Blade compiles failures into confusing line numbers in storage/framework/views.
Extension points to replicate. The bundle's real design contribution is that markup is a view concern you can override. Mirror it in Laravel: a component class holds defaults and type mapping, the Blade view holds markup, and you can override the view without touching call sites. That's the durable lesson from a package that is otherwise a museum piece.
Bottom line: don't add this to composer.json. Spend twenty minutes reading Resources/views/, port the two components, and move on.
How can I help you explore Laravel packages today?