Next.js Discord

Discord Forum

Dynamic Layouts With Dynamic Global Styles

Unanswered
necm1 posted this in #help-forum
Open in Discord
Just curious if it's possible to create a dynamic layout for the same route including different global stylesheets (linked to the layout) on a server component. Webpack seems to be ignoring conditional imports like:

packages/layout-test/src/layout.tsx
const TestLayout = ({children, loadStylesheet = false): PropsWithChildren<{loadStylesheet: boolean}>) => {
  if (loadStylesheet) {
    import('./style.scss');
  }  

  return <html>
    <body>
      WOW!
      {children}
    </body>
  </html>
} 


packages/layout-test2/src/layout.tsx
const Test2Layout = ({children, loadStylesheet = false): PropsWithChildren<{loadStylesheet: boolean}>) => {
  if (loadStylesheet) {
    import('./style.scss');
  }  

  return <html>
    <body>
      WOW 2!
      {children}
    </body>
  </html>
} 


apps/next/src/app/layout.tsx
import { TestLayout } from '@app/layout-test/layout';
import { TestLayout2 } from '@app/layout-test2/layout';

const layouts = {
  1: TestLayout
  2: TestLayout2
}

const SelectedLayout = async (layout: number, children) => {
  if (!layout || !layouts[layout]) {
    notFound();
  }

  const LayoutComponent = layouts[layout];

  return <LayoutComponent>{children}</LayoutComponent>
}

export default function RouteLayout({children}: PropsWithChildren) {
  // some conditions here
  // some informations here
  // some logic

  // fake data from a endpoint
  const layout = 1;

  return <SelectedLayout layout={layout}>{children}</SelectedLayout>
}


Just wrote the code on the fly. It works somehow, except for the style imports, because webpack tries to bundle it first, so they get mixed up together depending on the order, even if they aren't called. Seems to be a strange behaviour with webpack and imports.

Example with mixing up:

packages/layout-test/src/style.scss
body {
 @apply bg-red-500
}



packages/layout-test2/src/style.scss
body {
 @apply bg-green-500
}


You would expect to have the bg-red-500, which is somehow there, but gets overwritten by bg-green-500, due to webpack trying to recursive load all imported files have them ready for the built. Using dynamic doesn't work either. I don't even know if this approach is possible in Next or React in general.

Would be really interesting if anyone else tried something like this.
(and yes, trying to load the layouts with import('PATH') has the same effect as importing them on top - if-clause gets ignored (when you try to debug, it will simply logs the TestLayout if condition, because we passed it to SelectedLayout)

19 Replies

Barbary Lion
Most of the time when I want to use different styles I use something like this for classnames:

https://www.npmjs.com/package/clsx

Or for tailwind components:

https://www.tailwind-variants.org
Works both server and client
but the issue here is, that all of them have own components, own tailwind configurations which seems to be loading great
but the biggest issue is the webpack css import handling
which somehow interferes the whole logic / process of having dynamic layouts & their own global stylings
Barbary Lion
Maybe this is something to load your layours with
Tried different approaches, but the whole webpack and server component thing drives me crazy. If I choose to load the Layout styles from a client component, then the page would have NO STYLESHEETS until the dom is loadded and afterwards the styles.
so it would look like a blank undesigned page and after like 0.2s it load the whole styling part. That's why I'm forced to use server components, so the style is loaded before the dom could be served to the client
Loading a layout is not an issue, only the webpack css-loader part, which ignores conditonal imports for the build time and just pack it together with the same chunk as the main layout.tsx style would be
kinda funny
webpack ignores conditional import of style

layout-test/layout.tsx
if (loadStylesheet) {
  // this gets loaded
  import('./style.scss');
}


layout-test2/layout.tsx
if (loadStylesheet) {
  // this gets loaded but loadStylesheet = false
  import('./style.scss');
}


due to the import order, the style of TestLayout gets overwritten by Test2Layout, because it gets loaded into the same chunk (main-layout.css) by webpack
for example if I had a global css file in my RouteLayout (main-layout.css) it gets loaded into a chunk by webpack and now due to my "lazy-load" import in the same file (apps/src/layout.tsx), all styles which webpack css-loader detects will be written in the same css chunk as the RouteLayout (main-layout.css)
and the main-layout.css would look like this then:

// STYLE OF apps/layout.tsx which in this case doesn't exist because of the lazy-load Layout / Server Components delivering their own global styles

// compiled style.scss from TestLayout (which I want being here, because I passed it like that)
body {
   background-color: red;
}

// NOW strange part here - TestLayout2 css which I didn't want being here, but it is here even if loadStylesheet = false

body {
   background-color: green;
}


and now its overwritten
by an server component, which basically shouldn't load their own style file in the first place :D
The only way to achieve this goal is by simply handling the following "Layout"-Components as ClientComponents, which renders the whole DOM and then adds the needed stylesheet
but it doesn't seem right to me