Laravel Component to display Tailwind Tables

In our recent Projects we have been using Breeze & Jetstream, both of which uses TailwindCss. It is a common scenario to display the Data in Tabular Form on multiple Screen. Using TailwindCss, there were a lot of duplicate CSS Classes and bloated HTML. In this article we will see how we overcame those challenges.

We have a Product Model. And we need to create a Screen where we can display all the Products in a Table using Pagination. So we created a Route like below:

Route::get('/products', [ProductController::class, 'index'])
    ->middleware(['auth'])
    ->name('products');

We created a Controller which returned the Products by querying the DB.

class ProductController extends Controller
{
    //
    public function index() 
    {
        $products = Product::paginate();
        return view('product.index', [
            'products' => $products
        ]);
    }
}

And then we finally created the View where we looped through the Products and display them one by one in a table. Our final view looked like below:

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Products') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    <table>
                        <thead>
                            <tr>
                                <td>Name</td>
                                <td>SKU</td>
                                <td>Category</td>
                                <td>Status</td>
                            </tr>
                        </thead>
                        @foreach($products as $product)
                            <tr>
                                <td>{{$product->name}}</td>
                                <td>{{$product->sku}}</td>
                                <td>{{$product->category}}</td>
                                <td>{{$product->status ? 'Active' : 'Not Active'}}</td>
                            </tr>
                        @endforeach
                    </table>

                    <div class="mt-6">
                        {{$products->links()}}
                    </div>

                </div>
            </div>
        </div>
    </div>
</x-app-layout>

Here we are only interested in the table tag and everything inside it. At this stage our output looks like below:

Outupt without CSS

There are no margins, borders or padding like you would normally associate with a table. Well in TailwindCss, we have to apply them on our own. So we will apply some classes so that our table HTML looks like below:

                    <table class="w-full whitespace-no-wrapw-full whitespace-no-wrap">
                        <thead>
                            <tr class="text-center font-bold">
                                <td class="border px-6 py-4">Name</td>
                                <td class="border px-6 py-4">SKU</td>
                                <td class="border px-6 py-4">Category</td>
                                <td class="border px-6 py-4">Status</td>
                            </tr>
                        </thead>
                        @foreach($products as $product)
                            <tr>
                                <td class="border px-6 py-4">{{$product->name}}</td>
                                <td class="border px-6 py-4">{{$product->sku}}</td>
                                <td class="border px-6 py-4">{{$product->category}}</td>
                                <td class="border px-6 py-4">{{$product->status ? 'Active' : 'Not Active'}}</td>
                            </tr>
                        @endforeach
                    </table>

Here we have applied following classes:

  1. We have given classes to the top most element so that it takes full width & to prevent the text from wrapping within an element.
  2. We have given border to each td element as well as vertical & horizontal padding.
  3. Also we made heading as bold & center aligned.

Our output now looks like below:

with Tailwind CSS

This looks much much better. Thanks to the TailwindCss.

However, there are still some issues with the code. Our classes are repeating multiple times. What if we have to change the horizontal padding from px-6 to px-8. We will have to make changes at multiple places, not only on this screen but also on other screen where this table might be used.

In order to solve this problem, we will follow the same approach as is being followed by Breeze & Jetstream Packages. We will extract the common code into a component and use the component. In our case we are using Breeze and all our components are located at /resources/views/components directory. We will create a component for td and we will call this file table-column.blade.php. It will have the following code:

<td class="border px-6 py-4">
    {{$slot}}
</td>

Please see that we are using the $slot variable. This will have the value which is passed between the tags. We can use the component prefixing with x-. So in our case we can use x-table-column tag. We can now simplify our table HTML as:

                    <table class="w-full whitespace-no-wrapw-full whitespace-no-wrap">
                        <thead>
                            <tr class="text-center font-bold">
                                <x-table-column>Name</x-table-column>
                                <x-table-column>SKU</x-table-column>
                                <x-table-column>Category</x-table-column>
                                <x-table-column>Status</x-table-column>
                            </tr>
                        </thead>
                        @foreach($products as $product)
                            <tr>
                                <x-table-column>{{$product->name}}</x-table-column>
                                <x-table-column>{{$product->sku}}</x-table-column>
                                <x-table-column>{{$product->category}}</x-table-column>
                                <x-table-column>{{$product->status ? 'Active' : 'Not Active'}}</x-table-column>
                            </tr>
                        @endforeach
                    </table>

We see that all references to td along with classes border px-6 py-4 have been replaced by x-table-column. Our output still looks like the same, but now if we have to change any class we only need to make change at 1 place inside component file.

We can further utilize the capabilities of Anonymous Component and create a Component for Table itself. We will create a component file table.blade.php. It will have 2 slots. One would be the Header Slot containing everything in the thead tag. And other would be the default slot which will have all the other rows.

Our table.blade.php component file will look like:

<table class="w-full whitespace-no-wrap">
    <thead>
        <tr class="text-left font-bold">
            {{$header}}
        </tr>
    </thead>
    <tbody>
        {{$slot}}
    </tbody>
</table>

And now we can further simplify our final HTML as follows

                    <x-table>
                        <x-slot name="header">
                            <x-table-column>Name</x-table-column>
                            <x-table-column>SKU</x-table-column>
                            <x-table-column>Category</x-table-column>
                            <x-table-column>Status</x-table-column>
                        </x-slot>
                        @foreach($products as $product)
                            <tr>
                                <x-table-column>{{$product->name}}</x-table-column>
                                <x-table-column>{{$product->sku}}</x-table-column>
                                <x-table-column>{{$product->category}}</x-table-column>
                                <x-table-column>{{$product->status ? 'Active' : 'Not Active'}}</x-table-column>
                            </tr>
                        @endforeach
                    </x-table>

If you see our final HTML in the view does not contain any Tailwind Classes. All the Classes have been moved to components. So we have avoided the duplication of Classes as well as simplified our HTML by using Anonymous Components.

Hope this article was helpful to you.

If you are interested in the Video explanation, you can watch it on our YouTube Channel.

Leave a Reply

Your email address will not be published. Required fields are marked *