Simple Laravel Tabs

A nice simple approach to doing tabs

Simple Laravel Tabs

As more features get added to an app the UI can grow ever more confusing and unwieldy. A popular UI pattern is to split pages into sub sections behind tabs.

This is a quick note about how we added UI tabs to our Laravel based in-house CRM, Kraken. We started with a fairly over-engineered solution but ended up with something incredibly simple.

Let's take as an example a model we have in our app: Seminar (basically a training workshop). We want to split the management of this model across various tabs.

Our first approach was to setup routes for each tab and create a SeminarTabController to handle them. Because we wanted to have around 5 tabs for quite a few of our models this led to dozens of extra routes and several extra controllers in our app.

Over time we simplified our implementation until we arrived at what we have today. It turns out that all those routes and controllers were totally unnecessary, we could achieve the same thing with one extra line in the Model's controller and a small array in the Blade template.

In the controller we check to see if the browser is requesting a tab, if it is we pass which one to the view:

public function show(Seminar $seminar)
{
    $tab = request()->tab;
    return view('seminars.seminar', compact('seminar', 'tab'));
}

In the view we check if a tab is being requested and include that sub-view otherwise if no tab is requested we include the default tab:

@empty ($tab)
    @include('seminars.tabs.attendees')
@else
    @include("seminars.tabs.{$tab}")
@endempty

To provide the tab UI we setup an array called $tabs in the view. Each array item defines the name of the tab and the action:

@php($tabs = [
    'Booked' => action('SeminarController@show', $seminar),
    '😀 Faces' => action('SeminarController@show', [$seminar, 'tab' => 'faces']),
    '🛂 Check-in' => action('SeminarController@show', [$seminar, 'tab' => 'checkin']),
    'Email' => action('SeminarController@show', [$seminar, 'tab' => 'email']),
    "Scores ({$seminar->nps()})" => action('SeminarController@show', [$seminar, 'tab' => 'scores']),
    'Photos' => action('SeminarController@show', [$seminar, 'tab' => 'photos']),
    'Edit' => action('SeminarController@edit', $seminar),
])

Then, in our master layout template we have a section that checks if the $tabs array exists and outputs the HTML:

@isset($tabs)
<nav>
    @foreach ($tabs as $tab => $action)
        <a href="{{ $action }}"> {{ $tab }} </a>
    @endforeach
</nav>
@endisset

And that's it! No need for all those controllers and routes. The only downside is that the URLs don't look quite as nice because we're using a query string like ?tab=checkin rather than a nice clean /checkin but most modern browsers hide these details anyway nowadays and it's a perfectly good tradeoff for getting rid of a load of code.