Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple layouts, multiple CSS, only one at time

On my application I have multiple layouts. They are separated in different folders. I would like to use different SCSS files for the layouts. However no matter what layout is in use (based on a prop on router meta), all the SCSS files are imported. This messes up the layouts because the styles got mixed. Is there a way of import only the SCSS related to the layout on use?

App.vue

<template>

    <component :is="layout" />

</template>

<script>
import DefaultLayout from '@/layouts/default/DefaultLayout';
import PrimaryLayout from '@/layouts/primary/PrimaryLayout';
import SecondaryLayout from '@/layouts/secondary/SecondaryLayout';

export default {
    components: {
        DefaultLayout,
        PrimaryLayout,
        SecondaryLayout
    },
    data() {
        return {
            layout: null
        }
    },
    watch: {

        $route(to, from) {

            document.title = to.meta.title;

            this.layout = to.meta.layout || 'DefaultLayout';

        }

    }
};
</script>

src/layouts/primary/PrimaryLayout.vue

<template>
     Html content here
     <router-view />
</template>

<script>
export default {
    name: 'PrimaryLayout'
}
</script>

<style lang="scss">
@import '@/assets/layouts/primary/scss/main.scss';
</style>

src/layouts/secondary/SecondaryLayout.vue

<template>
     Different html content here
     <router-view />
</template>

<script>
export default {
    name: 'SecondaryLayout'
}
</script>

<style lang="scss">
@import '@/assets/layouts/secondary/scss/main.scss';
</style>

src/router/index.js

import { createRouter, createWebHistory } from 'vue-router';

const routes = [
     {
        path: '/login',
        name: 'Login',
        component: () => import('@/pages/auth/Login.vue'),
        meta: {
            layout: 'PrimaryLayout',
            title: 'Auth Page'
        }
    },
    {
        path: '/admin',
        name: 'Admin',
        component: () => import('@/pages/admin/Admin.vue'),
        children: [
             {
                 path: '',
                 name: 'Dashboard',
                 component: () => import('@/pages/admin/Dashboard.vue'),
                 meta: {
                     layout: 'SecondaryLayout',
                     title: 'Admin | Home'
                 },
             },
             ... other pages ...
        ]
    }
]

Can aynone give me a help?

like image 214
andrethedev Avatar asked Jun 26 '26 14:06

andrethedev


1 Answers

If the application has been generated using Vue CLI and uses Webpack under the hood, we can rely on Webpack's code splitting functionality by switching to dynamic imports:

So instead of

<script>
import DefaultLayout from '@/layouts/default/DefaultLayout';
import PrimaryLayout from '@/layouts/primary/PrimaryLayout';
import SecondaryLayout from '@/layouts/secondary/SecondaryLayout';

export default {
    components: {
        DefaultLayout,
        PrimaryLayout,
        SecondaryLayout
    },
    ...
};
</script>

we import layout components like this:

<script>
export default {
    components: {
        DefaultLayout: () => import('@/layouts/default/DefaultLayout'),
        PrimaryLayout: () => import('@/layouts/primary/PrimaryLayout'),
        SecondaryLayout: () => import('@/layouts/secondary/SecondaryLayout')
    },
    ...
};
</script> 

This should instruct Webpack and Vue Loader to put code for those components into separate bundles and only load the ones which are currently necessary.

Caution!

Users navigating from route with Layout A to the route with Layout B will end up with styles for both layouts loaded and applied. To guarantee true isolation is it better to use scoped or module styles.

Alternative

Another (and in my opinion a better) way of assigning layouts to different pages is to use slots.

This will result in a more concise code as there will be no need to declare meta properties for the routes and to watch them in App.vue. Code splitting would also work OOTB in the given case, as webpack dynamic imports are already used for view components in the router.

App.vue

<template>
  <router-view></router-view>
</template>

PrimaryLayout.vue

<template>
    ... Layout-specific HTML here
    <slot></slot>
    ... more layout-specific HTML
</template>

Login.vue

<template>
  <PrimaryLayout>
    ... Login.vue component template goes here
  </PrimaryLayout>
</template>

<script>
import PrimaryLayout from '@/layouts/primary/PrimaryLayout';

export default {
  components: { PrimaryLayout }
}
</script>
like image 137
olko Avatar answered Jun 29 '26 03:06

olko



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!