robbiechicago's avatar

Responsive layouts with components

Hi gang,

It feels like there's an obvious answer to this but I'm struggling to find it.

(I'm using Tailwind and Livewire)

I have created an app which is using a load of components on the page, and I've a completely different layout for each screen size. The xs and sm layouts are a two column grid with a mix of 1 and 2 column width components, and going up through md, lg and xl, columns get added and the components moved and resized to make it all look awesome. I'm happy with how it looks.

However, my solution for all this is to have separate page-level components for each screen size. So on my homepage I show and hide (using hidden and block tailwind classes) xs.blade.php, sm.blade.php etc.

This means that performance is shocking, because it's loading all the components five times, one for each page layout, even though only one layout is being displayed.

With components changing width and position so much between screen sizes, using flex and order is really not an option.

My only other possible solution was to use Alpine to work out the screen size and dynamically pull in the appropriate layout.

This feels like something people must have to deal with a lot, so there must be a decent, performant solution. Any thoughts from you clever people much appreciated.

0 likes
4 replies
LaryAI's avatar
Level 58

One possible solution to this problem is to use conditional rendering in your component to determine which layout to display based on the screen size. Instead of having separate page-level components for each screen size, you can create a single component that handles the responsive layouts.

Here's an example of how you can achieve this using Livewire and Tailwind CSS:

// app/Http/Livewire/ResponsiveLayout.php

use Livewire\Component;

class ResponsiveLayout extends Component
{
    public function render()
    {
        return view('livewire.responsive-layout', [
            'layout' => $this->getLayout(),
        ]);
    }

    private function getLayout()
    {
        $screenSize = $this->getScreenSize();

        if ($screenSize === 'xs') {
            return 'layouts.xs';
        } elseif ($screenSize === 'sm') {
            return 'layouts.sm';
        } elseif ($screenSize === 'md') {
            return 'layouts.md';
        } elseif ($screenSize === 'lg') {
            return 'layouts.lg';
        } elseif ($screenSize === 'xl') {
            return 'layouts.xl';
        }

        // Default layout
        return 'layouts.default';
    }

    private function getScreenSize()
    {
        // Use JavaScript to get the screen size
        // You can use a library like Alpine.js or write your own JavaScript code
        // to detect the screen size and pass it to the Livewire component

        // For example, using Alpine.js:
        // <div x-data="{ screenSize: window.innerWidth }">
        //     <span x-text="screenSize"></span>
        // </div>

        // Return the screen size based on your JavaScript implementation
        return 'xs';
    }
}
<!-- resources/views/livewire/responsive-layout.blade.php -->

<div>
    @if ($layout === 'layouts.xs')
        @include('layouts.xs')
    @elseif ($layout === 'layouts.sm')
        @include('layouts.sm')
    @elseif ($layout === 'layouts.md')
        @include('layouts.md')
    @elseif ($layout === 'layouts.lg')
        @include('layouts.lg')
    @elseif ($layout === 'layouts.xl')
        @include('layouts.xl')
    @else
        @include('layouts.default')
    @endif
</div>

In this solution, the ResponsiveLayout component determines the screen size using JavaScript and passes it to the Livewire component. Based on the screen size, the component selects the appropriate layout to render.

You would need to create separate Blade templates for each layout (layouts.xs.blade.php, layouts.sm.blade.php, etc.) and include them in the responsive-layout.blade.php template.

Note that you'll need to implement the JavaScript part to detect the screen size and pass it to the Livewire component. You can use a library like Alpine.js or write your own JavaScript code to achieve this.

This solution avoids loading all the components multiple times and dynamically renders the appropriate layout based on the screen size, improving performance.

webrobert's avatar

This feels like something people must have to deal with a lot

No because you can’t possibly know the screen size server side.

Personally I think designing better layouts that work dynamically is just an art. That is the work so to speak. Getting your layout to look good everywhere without being bloated. Or making extra trips to load correct layouts.

I like to manage less.

newbie360's avatar

For me, temp set a bg color, with Emmet(VScode has build-in) just type... and press keyboard TAB

.grid.grid-cols-1.sm:grid-cols-2.md:grid-cols-3.lg:grid-cols-4.xl:grid-cols-5.gap-4>.bg-red-200*10{$}

it will generate

<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
    <div class="bg-red-200">1</div>
    <div class="bg-red-200">2</div>
    <div class="bg-red-200">3</div>
    <div class="bg-red-200">4</div>
    <div class="bg-red-200">5</div>
    <div class="bg-red-200">6</div>
    <div class="bg-red-200">7</div>
    <div class="bg-red-200">8</div>
    <div class="bg-red-200">9</div>
    <div class="bg-red-200">10</div>
</div>

drag the browser right border to see the effect on difference screen size

robbiechicago's avatar

For interest, here's what I've implemented. Works nicely, and is based on the AI answer above.

Page class:

// App/Http/Livewire/Pages/Home.php

public $screenSize;

...

private function getLayout()
    {
        $prefix = 'livewire.pages.home.layouts.';
        if ($this->screenSize <= 640) {
            return $prefix . 'xs';
        } elseif ($this->screenSize <= 768) {
            return $prefix . 'sm';
        } elseif ($this->screenSize <= 1024) {
            return $prefix . 'sm';
        } elseif ($this->screenSize <= 1280) {
            return $prefix . 'lg';
        }
        return $prefix . 'xl';
    }

    public function render()
    {
        return view('livewire.pages.home.home', [
            'layout' => $this->getLayout(),
        ]);
    }

Page view:

// resources/views/livewire/pages/home/home.blade.php

<div>

    <div
        x-data="{ screenSz: @entangle('screenSize') }"
        x-init="screenSz = window.innerWidth"
    >
        <div x-on:resize.window="screenSz = window.innerWidth"></div>
    </div>

    @include($layout)

</div>
1 like

Please or to participate in this conversation.