CSS from multiple layouts is being included on individual pages

markplewis
0
markplewis
commented a year ago

Version

v1.4.2 - v2.4.2 (see below).

Reproduction link

https://github.com/markplewis/nuxt-layout-css-issue

Steps to reproduce

  1. Clone the following example repo.
  2. Run npm install then npm run dev.
  3. Navigate between the pages and inspect the CSS that's applied to the <body> element via your browser's dev tools.

https://github.com/markplewis/nuxt-layout-css-issue

What is expected ?

When a page specifies a layout, like this:

pages/index.vue:

<script>
  export default {
    layout: "home"
  }
</script>

Only the CSS from the corresponding layout file should be included in the page:

layouts/home.vue:

<style>
  body {
    background-color: steelblue;
  }
</style>

What is actually happening?

The CSS from every layout file is being included on every page, regardless of which layout is specified in the page's .vue file.

screenshot

Additional comments?

My example repo is using Nuxt Edge.

bug
0
manniL
6.9k
manniL
commented a year ago

Can confirm this bug (production).

"Workaround" which only hides the problem and works for just 1st render ☹️ :

 build: {
    splitChunks: {
      layouts: true
    }
}
0
markplewis
0
markplewis
commented a year ago

This is also a problem in Nuxt v1 (see the v1 branch of my demo repo). Styles from multiple layouts are being pulled into the page, but at least I'm not seeing styles from the default layout anymore:

screen shot 2018-09-12 at 7 55 33 am

On initial page load, the CSS is correct. It's only after I navigate to "page 2" then back again that the CSS from the other layout ends up on the page.

0
Legym
0
Legym
commented a year ago

It appears that vue-loader, the actually dependency vue-style-loader, is parsing the scss in each component (regardless of what component is being mounted) and is inlining them into the html document. I am currently running into this problem with themes in different layouts

https://github.com/nuxt/nuxt.js/issues/3852

My solution was to include a css file in the layout and have a hook to prevent themes from leaking into one another. I had to pull in a gulp file and have a separate build to compile my scss.

Layout A:

export default {
  name: 'LayoutA',
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/LayoutA/app.min.css' }
      ],
      bodyAttrs: {
        class: 'LayoutA'
      }
    }
  }
}
</script>

Layout B:

export default {
  name: 'LayoutB',
  head() {
    return {
      link: [
        { rel: 'stylesheet', href: '/css/LayoutB/app.min.css' }
      ],
      bodyAttrs: {
        class: 'LayoutB'
      }
    }
  }
}
</script>
0
hareku
18
hareku
commented a year ago

I have same issue.

0
Legym
0
Legym
commented a year ago

@hareku I was able to solve my issue using

 build: {
    splitChunks: {
      layouts: true
    }
}
0
hareku
18
hareku
commented a year ago

@Legym
Thanks!!!

0
markplewis
0
markplewis
commented a year ago

I upgraded my demo project to Nuxt 2.1.0 and I'm still experiencing this problem. As @manniL stated above, adding splitChunks: { layouts: true } solves the problem for first render (i.e. loading the page directly or hard refreshing your browser), but the problem persists when navigating back and forth between pages after that.

0
Legym
0
Legym
commented a year ago

@markplewis I had a different folder setup than you. We created a theme system inside our nuxt build and each theme was locked into their own URL. We didn't go from one theme to another directly and so we didn't run into your issue.

screen shot 2018-10-02 at 7 50 31 pm

splitChunks: { layouts: true } did solve our issue because originally our styles in Layout would bleed into other themes. I.e Styles from Desktop were leaking into Mobile.

However I would say the issue you are having is definitely a bug. The only suggestion I have is to do something like this or include it's own css file until it's fixed.

Layout 1

<template>
  <div>
    <nuxt/>
  </div>
</template>

<script>
export default {
  head() {
    return {
      bodyAttrs: {
        class: 'layout1'
      }
    }
  }
}

</script>

<style>
  .layout1 {
    background-color: steelblue;
  }
</style>

Layout 2

<template>
  <div>
    <nuxt/>
  </div>
</template>

<script>
export default {
  head() {
    return {
      bodyAttrs: {
        class: 'layout2'
      }
    }
  }
}

</script>

<style>
  .layout2 {
    background-color: steelblue;
  }
</style>

Vue-style-loader will take any css in the <style> tags and inline them to the document. Apparently vue-style-loader doesn't remove inline styles when you switch back so the last inline css always takes effect. You'll need to bind to a node that changes when switching layouts.

0
sumeshkmp
0
sumeshkmp
commented a year ago

This issue is reproducible in Nuxt 2.2.0 when loading the layout dynamically.

build: {
splitChunks: {
layouts: true
}
}

Did not worked.

screen shot 2018-10-15 at 11 57 52 am

0
fbrbovic
17
fbrbovic
commented a year ago

Still experiencing this problem too, any solutions? , layouts true doesn't fix it.

0
Atinux
28.8k
Atinux
commented a year ago

We are currently working on a solution that could work without any changes, for both critical CSS and extractCSS modes. Stay tuned :)

0
fbrbovic
17
fbrbovic
commented a year ago

Thanks, my temporary solution was to import scss or styl file inside <script> tag which doesn't seem to leak css

0
hareku
18
hareku
commented a year ago

I want to use splitChunks with extractCSS option.

0
rikoz
0
rikoz
commented a year ago

Any solution yet, I'm experiencing same here. Please I'm seeing a lot of temporaty fix but no detailed explanation of them. I need your help

Thanks

0
jaredreich
3
jaredreich
commented a year ago

Issues that are labeled as 🕐Pending will not be automatically marked as stale.

Oops Stale Bot - you had one job! 😂

Really excited for the Nuxt team to fix this one. Go team go! 😄
(please don't stale/expire this issue…)

0
menthol
5
menthol
commented a year ago

I find a way to make it work on production, (no way yet for development).

in nuxt.config.js

import crypto from 'crypto';

const BUILDID = crypto.randomBytes(10).toString('hex');

module.exports = {
   // ...
  build: {
    extractCSS: true,
    // ...
    filenames: {
      css: `${BUILDID}.[name].css`,
    },
    // ...
  },
  env: {
    BUILDID,
  },
};

in layouts/*.vue

<template>
  <nuxt />
</template>
<script>
  // Fake import, make extract css create a new file with specific chunkName
  export function css() { import(/* webpackChunkName: 'theme-default' */ '~/assets/style.css'); }

  export default {
    head() {
      return {
        link: [
          // integrate compiled file into current layout
          { type: 'text/css', rel: 'stylesheet', href: `/_nuxt/${process.env.BUILDID}.theme-default.css` },
        ],
      };
    },
  };
</script>

As the dev server use in-memory compilation, i wasn't able to find the correct path for integrate compiled css file into current page in development. If you want to try, make sure you freeze the extractCSS property, ex: ...Object.defineProperty({}, 'extractCSS', { value: true }),;

0
menthol
5
menthol
commented a year ago

So, after some investigation, I succeeded make it work in dev :

in nuxt.config.js

import crypto from 'crypto';

const BUILDID = crypto.randomBytes(10).toString('hex');

module.exports = {
  // ...
  modules: [
    // ...
    '~/modules/ForceExtractCSS',
  ],
  // ...
  build: {
    extractCSS: true,
    // ...
    filenames: {
      css: `${BUILDID}.[name].css`,
    },
    // ...
  },
  env: {
    BUILDID,
  },
};

in modules/ForceExtractCSS.js

export default function ForceExtractCSSModule() {
  this.nuxt.hook('ready', () => {
    this.options.build.extractCSS = true;
  });
}

and in layouts/*.vue

<template>
  <nuxt />
</template>
<script>
  // Fake import, make extract css create a new file with specific chunkName
  export function css() { import(/* webpackChunkName: 'theme-default' */ '~/assets/style.css'); }

  export default {
    head() {
      return {
        link: [
          // integrate compiled file into current layout
          { type: 'text/css', rel: 'stylesheet', href: `/_nuxt/${process.env.BUILDID}.theme-default.css` },
        ],
      };
    },
  };
</script>

Canvas : HMR isn't working, head() isn't recalculated after HRM call

0
fbrbovic
17
fbrbovic
commented a year ago

This is still broken in 2.4.0

0
b02505048
0
b02505048
commented a year ago

Yeah, still a problem in 2.4.0

0
markplewis
0
markplewis
commented a year ago

FYI: I just updated my demo project to Nuxt 2.4.3.

0
nick-lawrence
0
nick-lawrence
commented a year ago

@markplewis Are you still experiencing this issue in the latest version?

I am currently in the middle of build using Nuxt 2.4.0 and undesired multiple Layout/CSS cross-compilation is still an issue.

0
markplewis
0
markplewis
commented 10 months ago

@nick-lawrence I just upgraded my demo project to Nuxt 2.5 and I'm still experiencing this issue.

0
ryanwinchester
0
ryanwinchester
commented 10 months ago

So, I wanted my login page to have a coloured background and the default layouts to have a white background.

This silly workaround does the trick for me.

  // login.vue
  beforeCreate() {
    document
      .getElementsByTagName('html')[0]
      .setAttribute('style', 'background-color: #4fc08d')
  }
  // default.vue
  beforeCreate() {
    document.getElementsByTagName('html')[0].removeAttribute('style')
  }
0
TomFrajer
0
TomFrajer
commented 10 months ago

Default.vue layout should have <style scoped> in it AND all other custom layouts should NOT HAVE scoped styles… this fixed it for me.

0
er1x
0
er1x
commented 10 months ago

+1 (Nuxt v 2.4.0)

In my case not only the layout "leaks" CSS, "pages/index.vue" too.
I'm adding an extra layout and extra pages (using this layout) through a Nuxt module, as a some sort of sub-application:

this.addLayout(path.resolve(__dirname, 'custom/layout.vue'), 'custom')
this.extendRoutes((routes, resolve) => {
  routes.push({
    path: '/subapp',
    name: 'subapp',
    component: resolve(__dirname, 'custom/subapp.vue')
  })
})
0
padinko
5
padinko
commented 10 months ago

my workaround for now is

nuxt.config.js

build: {
    splitChunks: {
      layouts: true
    }
}

and then reload page beafore each layout change, mixin in all layouts:

    beforeCreate() {
        if (!process.server) {
            if (window.__NUXT_LAYOUT__ && window.__NUXT_LAYOUT__ !== this) {
                window.location.reload();
            }
            window.__NUXT_LAYOUT__ = this;
        }
    },
1
christopherblack
0
christopherblack
commented 4 months ago

Hello!
Is there any solution yet? Still experiencing the issue in Nuxt 2.9.2

0
donni106
0
donni106
commented 3 months ago

We have the same problem. It seems to work for us with scoping every layout <style>s. Can there be a problem with <style scoped> per layout?

0
fachreza73
0
fachreza73
commented 3 months ago

+1

Nuxt 2.10.1

0
UtopiaBe
0
UtopiaBe
commented 3 months ago

I am having 2 languages, with RTL and LTR…
So i wanted to build 2 layouts to load RTL.CSS or LTR.CSS
And it uploads both of them :( so the website brokes…

Waiting for a solution.

0
matrunchyk
0
matrunchyk
commented 3 months ago

Without this feature layouts are unusable for our projects…. Can we help you guys somehow?

0
SHxKM
0
SHxKM
commented 2 months ago

Any progress on this? I was sure I was doing something wrong. It's pretty much useless to use more than one layout if CSS is getting merged. scopeing default didn't help in my case.

0
paulvonber
0
paulvonber
commented 2 months ago

Without this feature layouts are unusable for our projects…. Can we help you guys somehow?

exactly same here, we stuck totally with this, I already blaming myself for switching to nuxt.
Overall layout styles should be scoped for everything what is included in that particular layout, otherwise they are just unusable.

0
necony286
0
necony286
commented a month ago

is this going to be fixed anytime soon please?

0
matrunchyk
0
matrunchyk
commented a month ago

We are currently working on a solution that could work without any changes, for both critical CSS and extractCSS modes. Stay tuned :)

Dear @Atinux, @alexchopin, @manniL, would you mind to give some feedback on this ticket. I'm sorry I'm and others bothering you here, but as you could probably see, this is a very important feature for us. For someone like me, it's a show-stopper.

Please note, that I'm just asking about some feedback, not the implementation itself. For a project with 24k stars a year w/o a feedback doesn't make it trustworthy.

Have you started working on it? Could you please share some details of the implementation so we could potentially join? Or if you haven't, we would know that we can start our own research and potentially propose some PR?

Again, thank you guys for all you work you're doing here! This is very worthy and today it is the best SSR framework for Vue. Sorry for my impatience and curiosity.

0
MLDMoritz
0
MLDMoritz
commented a month ago

I would love to help too, if there's anything people can do please tell us.

0
gambolputty
0
gambolputty
commented 16 days ago

This is still broken in 2.11.0

0
julpat
0
julpat
commented 15 days ago

I think this is related with Issue with template styles not unmounting on programmatic navigation #2561 and the problem is in vue-loader when does not remove unused css in different layout/page (when you have build.splitChunks.layouts === true ).

0
gambolputty
0
gambolputty
commented 15 days ago

Another workaround could be to assign a class to the <body>-element (or another ancestor) and make sure, that all layout-related styles are part of that class-scope, e. g.

body.layout1 .menu {  }

If this is not fixable, could somebody mention this limitation in the docs (inheritance of css styles between layouts)?

0
Atinux
28.8k
Atinux
commented 14 days ago

Hi there,

Sorry for the late answer and happy new year at the same time!

Actually one possibility is to use @gambolputty suggestion with bodyAttrs:

<script>
// layouts/dark.vue
export default {
  head: {
    bodyAttrs: {
      class: "dark-theme"
    }
  }
};
</script>

And to prefix your style with .dark-theme .my-class { ... }.

Please find a live example here: https://codesandbox.io/s/nuxt-layouts-theming-ru954

We will beta test Vue 3 to test better layout scoping, will keep this thread updated.

0
aloiki
0
aloiki
commented 11 days ago

I am using nuxt v.2.10 for a project and unfortunately it does not work with style files included in different layouts.

Layout A:

<template />

<style lang="scss">
  @import '@/assets/scss/layout-a.scss';
</style>

Layout B:

<template />

<style lang="scss">
  @import '@/assets/scss/layout-b.scss';
</style>

I tried it with the options in nuxt.config splitChunks.layout and extractCss but both configurations do not work. If the user switches the layout from Layout A to Layout B
the style of the former layout is loaded. Our workaround at the moment is a hard page reload.

0
Informations
Bug ReportOpen
#c7723 - Created a year ago