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

Tabs Laravel Package

mrshanebarron/tabs

mrshanebarron/tabs is a Laravel package for building tabbed interfaces in Blade views. Define tabs in PHP, render accessible markup, and switch panels without custom JavaScript. Lightweight, configurable, and easy to drop into any Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with Composer and let package discovery register the provider:
    composer require mrshanebarron/tabs
    
    If your app disables discovery, add the provider to config/app.php manually.
  • Look in vendor/mrshanebarron/tabs/src and vendor/mrshanebarron/tabs/resources/views before writing markup. Component names, slot names, and the shape of the tab definition API are the things you need to get right on day one, and they're five seconds away in the vendor tree (php artisan vendor:publish --list will also show the available publish tags).
  • Publish the views so you can style them, and publish the config:
    php artisan vendor:publish --provider="MrShaneBarron\\Tabs\\TabsServiceProvider" --tag="views"
    php artisan vendor:publish --provider="MrShaneBarron\\Tabs\\TabsServiceProvider" --tag="config"
    php artisan config:clear && php artisan view:clear
    
    v1.0.1 fixed config file naming with the sb- prefix — expect config/sb-tabs.php, and read it with config('sb-tabs.key'). If you're coming from 1.0.0, check config/ for a stale unprefixed file and delete or merge it rather than leaving both.
  • First use case: a settings or profile page with three panels (Profile / Password / Notifications) that you want switchable with zero build step. Render the container component with a small set of tab definitions, put your existing Blade partials in the panels, and confirm the active tab highlights correctly on /settings/profile before you spread the pattern to other pages.

Implementation Patterns

  • Define tabs declaratively in the view, keep content in partials. The package is a rendering concern, not a data layer. Build the tab set inline in the page Blade and @include (or <x-...>) a partial per panel so each panel stays independently testable and reusable.
  • Wrap the vendor components in your own. Create resources/views/components/app/tabs.blade.php that composes the package's components with your project's classes and defaults. Every page then uses <x-app.tabs>; when the package changes its markup or you restyle, you edit one file instead of forty pages.
  • Publish views once, then treat them as your code. Publishing puts the package Blade into resources/views/vendor/tabs. From that moment the package's own views are dead to you — which is fine, and is the intended extension point — but it also means upstream template fixes won't reach you. Diff the vendor views against yours on every upgrade.
  • Drive the active tab from the route, not from a flag you thread through. The whole value proposition is automatic active-state detection. Structure your routes so the URL identifies the tab (/settings/profile, /settings/password), and let the component resolve it. Reach for an explicit override only for genuinely non-URL state.
  • Deep-link with a query parameter when tabs aren't routes. For a dashboard with tabs on a single URL, bind the active tab to ?tab=billing via request()->query('tab'), validate it against a whitelist, and fall back to a default. That makes tabs shareable and back-button friendly without touching the package.
  • Keep panels cheap. Tabs are typically client-side visibility toggles — all panels render on every request. Wrap expensive panel content in caching or move it behind an Alpine/Livewire lazy load. Watch for N+1s that only exist because you added a fourth tab.
  • Forms: one <form>, many panels. Keep the entire tab set inside a single form element so hidden panels still submit their inputs. Never conditionally @if a panel away — a disabled or unrendered panel silently drops its fields on POST.
  • Test as HTTP, not unit. An integration test that hits the tabbed route and asserts the active tab's marker (assertSee on the active class or aria-selected="true") plus the presence of each panel's content catches the realistic regressions. Add a test per tab route so a rename can't quietly break active detection.
  • Accessibility is a shared responsibility. Confirm the rendered output has role="tablist", role="tab", aria-selected, and aria-controls wired to the panels. If you publish and restyle the views, preserve those attributes — they are the first thing lost in a markup rewrite.
  • Rollout flag for restyles. If you're replacing an existing tab implementation, gate the new markup behind a config boolean sourced from an env var so you can flip back without a redeploy.

Gotchas and Tips

  • The sb- prefix is part of the config key. config/sb-tabs.php is read as config('sb-tabs.default'), not config('tabs.default'). After the 1.0.1 rename, grep for stale accessors:
    grep -rn "config('tabs" app/ config/ resources/ tests/
    
  • vendor:publish never overwrites. Your published config and views are frozen at the version you published them. After an upgrade, compare the package stubs against your copies and merge new keys manually — missing config keys usually surface as silent null defaults, not exceptions.
  • Config and view caches hide your changes. If behaviour doesn't change after editing config, run php artisan config:clear; if markup doesn't change after editing a published view, run php artisan view:clear. In production, php artisan config:cache and php artisan view:cache belong in the deploy script after config and views are in place.
  • Duplicate tab IDs. The most common real bug: the component used twice on a page (or inside a @foreach) produces duplicate id/aria-controls pairs, and the wrong panel toggles. If the package doesn't accept an id/name prefix, pass the loop index or model key into a wrapper and namespace your own ids.
  • Overlapping route patterns break active detection. /settings matches /settings/profile under a loose prefix check. Prefer exact route-name matching (request()->routeIs('settings.profile')) over URL-prefix heuristics, and test the sibling routes explicitly.
  • Validation errors land in invisible panels. If a submit fails on a non-active tab, the user sees a page with no visible error. On redirect, flash the offending tab and open it server-side (or pass the first error key with @error checks per panel).
  • "No JS build step" also means no module lifecycle. If you use Turbo, Livewire wire:navigate, or an SPA-style router, panels can end up in a stale state after a partial page swap. Re-key the component or drive active state from a reactive property rather than assuming a fresh boot.
  • Debugging checklist: php artisan tinker then config('sb-tabs') to dump the resolved array; app()->make(...) to verify the container binding; php artisan vendor:publish --list to confirm tags (a tag mismatch is the #1 reason "the config file never appeared"); check bootstrap/cache/config.php exists if you suspect caching.
  • Never edit vendor/. All customisation belongs in published views, the published sb-tabs.php config, or your own wrapper component. Vendor edits disappear on the next composer update.
  • Pin the version and treat 1.0.1 as a migration, not a hotfix. A config filename change is a filesystem contract change; the patch number undersells it. Diff the published config before and after, clear caches, and smoke-test one tabbed route in staging before promoting.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
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