emotion-js/emotion

Plans to support Next.js 13 - /app directory

fcisio opened this issue Β· 168 comments

The problem

Next JS just release their v13 publicly.
As seen in their docs, emotion has not yet added support.

Is there any plan to add support in the near future?

Thanks.

It might be possible see about circling the wagons with the MUI and Vercel teams, as well, on this. MUI is very widely used (and teams are paying!). I am not sure what the specific numbers are, but I have to imagine we have a very large contingent of MUI/Emotion users overlapping with Next.js. Having these two titans not work together is a miss.

(I suspect if getting this working needed some sponsorship, $$$ could be found!)

We’re also super looking for this so Theme UI can support the app directory!

We may want to add an explicit API for this but this works today:

// app/emotion.tsx
"use client";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { useServerInsertedHTML } from "next/navigation";
import { useState } from "react";

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}) {
  const [cache] = useState(() => {
    const cache = createCache({ key: "css" });
    cache.compat = true;
    return cache;
  });

  useServerInsertedHTML(() => {
    return (
      <style
        data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(" ")}`}
        dangerouslySetInnerHTML={{
          __html: Object.values(cache.inserted).join(" "),
        }}
      />
    );
  });

  return <CacheProvider value={cache}>{children}</CacheProvider>;
}

// app/layout.tsx
import RootStyleRegistry from "./emotion";

export default function RootLayout({ children }: { children: JSX.Element }) {
  return (
    <html>
      <head></head>
      <body>
        <RootStyleRegistry>{children}</RootStyleRegistry>
      </body>
    </html>
  );
}

// app/page.tsx
/** @jsxImportSource @emotion/react */
"use client";

export default function Page() {
  return <div css={{ color: "green" }}>something</div>;
}

@mitchellhamilton did you get this working for you?

Trying in a StackBlitz just now, it seems like it's giving me an error about React.createContext not being a function:

StackBlitz: https://stackblitz.com/edit/vercel-next-js-mxnxa7?file=app%2Fpage.tsx,app%2Flayout.tsx,app%2FEmotionRootStyleRegistry.tsx,next.config.js

event - compiled client and server successfully in 59 ms (403 modules)
error - (sc_server)/node_modules/@emotion/react/dist/emotion-element-b63ca7c6.cjs.dev.js (20:47) @ eval
error - TypeError: React.createContext is not a function
    at eval (webpack-internal:///(sc_server)/./node_modules/@emotion/react/dist/emotion-element-b63ca7c6.cjs.dev.js:19:49)
    at (sc_server)/./node_modules/@emotion/react/dist/emotion-element-b63ca7c6.cjs.dev.js (/home/projects/vercel-next-js-mxnxa7/.next/server/app/page.js:501:1)
    at __webpack_require__ (/home/projects/vercel-next-js-mxnxa7/.next/server/webpack-runtime.js:33:43)
    at eval (webpack-internal:///(sc_server)/./node_modules/@emotion/react/jsx-dev-runtime/dist/emotion-react-jsx-dev-runtime.cjs.dev.js:7:22)
    at (sc_server)/./node_modules/@emotion/react/jsx-dev-runtime/dist/emotion-react-jsx-dev-runtime.cjs.dev.js (/home/projects/vercel-next-js-mxnxa7/.next/server/app/page.js:512:1)
    at __webpack_require__ (/home/projects/vercel-next-js-mxnxa7/.next/server/webpack-runtime.js:33:43)
    at eval (webpack-internal:///(sc_server)/./app/layout.tsx:5:88)
    at (sc_server)/./app/layout.tsx (/home/projects/vercel-next-js-mxnxa7/.next/server/app/page.js:403:1)
    at __webpack_require__ (/home/projects/vercel-next-js-mxnxa7/.next/server/webpack-runtime.js:33:43)
    at Object.layout (webpack-internal:///(sc_server)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fpage&appPaths=%2Fpage&pagePath=private-next-app-dir%2Fpage.tsx&appDir=%2Fhome%2Fprojects%2Fvercel-next-js-mxnxa7%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2Fhome%2Fprojects%2Fvercel-next-js-mxnxa7&isDev=true&tsconfigPath=tsconfig.json!:22:99) {
  type: 'TypeError',
  page: '/'
}
null

Screenshot 2022-10-27 at 19 46 47


It does seem to work if the layout component is made into a client component, but this would be unfortunate:

StackBlitz: https://stackblitz.com/edit/vercel-next-js-tbkg4a?file=app%2Flayout.tsx,app%2Fpage.tsx,app%2FEmotionRootStyleRegistry.tsx,next.config.js

Oh it seems like my optimization of removing the /** @jsxImportSource @emotion/react */ and using { compiler: { emotion: true } } (the SWC Emotion transform plugin) caused this to break (I guess this is still using context under the hood, will open an issue in Next.js repo).

Working StackBlitz, based on @emmatown's original example:

StackBlitz: https://stackblitz.com/edit/vercel-next-js-ajvkxp?file=app%2Fpage.tsx,app%2Flayout.tsx,app%2FEmotionRootStyleRegistry.tsx,next.config.js

Reported a bug about the SWC Emotion transform plugin here:

Just note that the presented solution works with the app directory - it still doesn't quite work with streaming. It's not exactly Emotion's fault though, other libraries won't work either because the callback provided to useServerInsertedHTML gets only called once. So it's only possible to inject styles contained in the initial "shell" this way.

You can observe this on this stackblitz that uses Styled Components. I've prepared it by copy-pasting the example from the Next.js docs, on top of that I've just added a single Suspense boundary to "chunk" the stream. The rendered span should have a red text but the whole thing stays green.

Thank you all so much for the samples & explanation! @Andarist, could you give a little more color on the long-term situation here? If Next resolves the SWC bug & Emotion does an update, where will that leave us with server component support? Are there aspects that are never going to work?

To the best of my understanding - we should be able to support server components in the future. Some parts of that are fuzzy to me though. Mainly I'm not sure how to avoid injecting the same styles back to the client on server component refetches. We rely on a list of inserted IDs but server components render for a particular request - where we don't have access to the IDs inserted by previous requests (or by the client, for that matter).

I too need Mui + emotions to work, this would greatly speed my migration to client/server component architecture

As of right now, I converted all the components into client components to β€˜migrate’ to nexjs13. πŸ˜‚ Need this before any meaningful migration

@mitchellhamilton Is cache.compat something exclusive to Emotion 10?
When I run this setup on the latest version I get TypeError: Cannot read properties of undefined (reading 'registered') and TypeError: Cannot read properties of undefined (reading 'compat')

To the best of my understanding - we should be able to support server components in the future. Some parts of that are fuzzy to me though. Mainly I'm not sure how to avoid injecting the same styles back to the client on server component refetches. We rely on a list of inserted IDs but server components render for a particular request - where we don't have access to the IDs inserted by previous requests (or by the client, for that matter).

Have you found any solutions yet? if you have can we kindly get a timeline for emotion-js working with nextjs13

@godfrednanaowusu I ported a next 12 project to next 13 and have not had any trouble with emotion and mui working correctly (besides a breaking change in next/link). The issues here appear to be about using the /app directory instead of /pages. Since /app is listed as beta anyways, perhaps this isn't such a major obstacle to using next 13 with a plan to migrate project structure at a later date.

Essentially by asking to use the react 18 features with /app, this is asking emotion to fully support react 18 fully, which is going to involve some pretty big structural changes to do right.

@MinervaBot read the title, the issue is about nextjs 13 app directory, not just upgrading and use pages folder

@unfernandito I noticed a lot of people were talking about needing this to use 13, which seemed to perhaps deserve some clarification, since /app is not even in the stable next.js docs yet.

After talking with the Next.js team and helping them recognize the problem with useServerInsertedHTML and Suspense that issue has been fixed in vercel/next.js#42293

With that fix Emotion roughly works in the /app if you do this:

"use client";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { useServerInsertedHTML } from "next/navigation";
import { useState } from "react";

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}) {
  const [{ cache, flush }] = useState(() => {
    const cache = createCache({ key: "my" });
    cache.compat = true;
    const prevInsert = cache.insert;
    let inserted: string[] = [];
    cache.insert = (...args) => {
      const serialized = args[1];
      if (cache.inserted[serialized.name] === undefined) {
        inserted.push(serialized.name);
      }
      return prevInsert(...args);
    };
    const flush = () => {
      const prevInserted = inserted;
      inserted = [];
      return prevInserted;
    };
    return { cache, flush };
  });

  useServerInsertedHTML(() => {
    const names = flush();
    if (names.length === 0) return null;
    let styles = "";
    for (const name of names) {
      styles += cache.inserted[name];
    }
    return (
      <style
        data-emotion={`${cache.key} ${names.join(" ")}`}
        dangerouslySetInnerHTML={{
          __html: styles,
        }}
      />
    );
  });

  return <CacheProvider value={cache}>{children}</CacheProvider>;
}

(kudos to the @emmatown for providing this implementation)

We need to figure out the exact APIs for handling this within Emotion but conceptually the thing would end up being very similar - you just won't have to override cache.insert in the future and we'll provide this new kind of the flush in Emotion itself.

Note that you should only use Emotion with the so-called client components (we might add "use client"; directive to our files to make this obvious). They can render on the server just fine - for the initial SSRed/streamed HTML but server "refetches" won't render them on the server, they might be rendered on the client when the "refetch" response comes back. This is not Emotion's limitation - it's a limitation for all CSS-in-JS libs like Emotion (including Styled Components, styled-jsx and more)

@Andarist Can you provide a CodeSandbox, please? I am getting the React.createContext is not a function error in the console even after wrapping my app with RootStyleRegistry

Happy to see progress on here!
I was wondering if anyone could see jsxImportSource working in compiler (ts swc or babel)?

From my understanding, I don't think it will be possible. Instead, we might have to use the /** @jsxImportSource @emotion/react */ comment (per file).

With Babel it's definitely possible - you might need to set the runtime: 'automatic' first though: https://babeljs.io/docs/en/babel-preset-react#runtime

Thanks @Andarist I'll test with Babel in a little bit.
I did test with TS SWC just now (with the stackblitz above), and that one doesn't seem to work.

ScreenShot 2022-11-18 at 10 57 46 AM

Note that you should only use Emotion with the so-called client components (we might add "use client"; directive to our files to make this obvious). They can render on the server just fine - for the initial SSRed/streamed HTML but server "refetches" won't render them on the server, they might be rendered on the client when the "refetch" response comes back. This is not Emotion's limitation - it's a limitation for all CSS-in-JS libs like Emotion (including Styled Components, styled-jsx and more)

@Andarist What does this mean? Surely we can add emotion styles to our react server components? What value does a react server component have when it doesn't have any styles? Maybe I don't fully understand :)

Yeah, this is a little bit confusing - essentially server components have some limitations (for good reasons, in this model). They also have some unique capabilities (such as being able to use async functions).

I hope that this answer can help to clarify the situation a little bit. Let me know if you have any further questions.

Hmm. Are we saying that Emotion will not be supporting styling server components? If so, we should be clear about this, as it is possibly reason enough to move off Emotion for some folks. My understanding is that server components do work with CSS modules, for example.

So for clarity's sake, we should probably not be discussing:

  • Data fetching components that do not involve CSS
  • MUI Base components that do not involve styling
  • MUI components that involve user interactivity (clearly need to be client components by definition)

The case that I think we're eager to understand is for basic static components that do things like layout and do require styling to achieve that. Do we currently β€” and will we long term β€” have a gap where CSS Modules could work for styling these components but Emotion would not?

The case that I think we're eager to understand is for basic static components that do things like layout and do require styling to achieve that. Do we currently β€” and will we long term β€” have a gap where CSS Modules could work but Emotion would not?

Correct me if I'm wrong, but my understanding is that server and client components will both "work" in a SSR context, the only difference being that client components will also re-render and hydrate the client-side React tree. If you want to style server components (i.e. components which do not hydrate client-side) because that performance gain is important to you, then that may be a reason for you to ditch Emotion. If you are OK with hydration as is the current status quo, then you have nothing to be concerned about.

Is this incorrect?

Yes, we are trying to understand if we can style server components (i.e. components which do not hydrate client-side). Before figuring out if this is a reason to ditch Emotion, we want to understand if this is in fact the near- to mid-term plan.

If you want to style server components (i.e. components which do not hydrate client-side) because that performance gain is important to you, then that may be a reason for you to ditch Emotion.

This the main benefit of RSC to offload work to the server instead of having to do it on the client and have a performance gain? This effectively makes emotion useless in the future? (and will create a few thousand hours of work to migrate to something else..)

Maybe this conclusion is premature, but I really don't follow. It can not be that emotioncss can not be used with react server components. Styling needs to happen in a RSC and the resulting HTML/CSS should be send to the browser. That is the whole idea behind server components?

If you want to style server components (i.e. components which do not hydrate client-side) because that performance gain is important to you, then that may be a reason for you to ditch Emotion.

This the main benefit of RSC to offload work to the server instead of having to do it on the client and have a performance gain? This effectively makes emotion useless in the future? (and will create a few thousand hours of work to migrate to something else..)

Maybe this conclusion is premature, but I really don't follow. It can not be that emotioncss can not be used with react server components. Styling needs to happen in a RSC and the resulting HTML/CSS should be send to the browser. That is the whole idea behind server components?

I think it's a misunderstanding. Next.js with SSR already does render styled components on the server. However styled components do not work with react server components. But styled components work in client components. Client components can be rendered server side with Next. So you already have a performance gain from that if that's what you're looking for. What React Server Components brings to the table is being able to natively call server functions.

You can verify this by writing a client component with emotion or styled components inside, and check your network when you have loaded your page in your browser. Check the reponse content of your first request which rendrs the html of your page, it already has your css.

React server Components are still new, I think it's a bit early to ditch styled components because they dont yet render in RSC.

we are trying to understand if we can style server components

The question is answered already here.

Server Components were designed in a way that is at fundamental odds with CSS-in-JS. So don't expect Emotion (or other popular CSS-in-JS libs) to introduce support for them. They just don't allow us to integrate with them anyhow.

I am considering the question, why do we use a library like Emotion or Material or Chakra, etc. in the first place. A big benefit we get is reactivity, i.e. we can link the styles directly with React state. This is what enables us to have dynamic light/dark mode settings without page reload, for example. I think it fundamentally will not work to use these libraries in RSC because there is no reactivity or state in RSC - i.e. the goals of these libraries are juxtaposed to the goals of RSC.

However - to repeat what has been said already - since server Components can render client components, this does not mean Emotion cannot be used with server components, it only means that Emotion cannot be used directly by server components, if this makes sense.

I'm using NextJS v13, emotion, MUIv5 and have a problem: While the page is loading(reload after cached), the css is broken(image huge size,...). How to fix?

Screen.Recording.2022-12-02.at.10.53.12.mov

I'm using NextJS v13, emotion, MUIv5 and have a problem: While the page is loading(reload after cached), the css is broken(image huge size,...). How to fix?

Screen.Recording.2022-12-02.at.10.53.12.mov

@Andarist This might be from hydrating <html>

Hmm. Are we saying that Emotion will not be supporting styling server components? If so, we should be clear about this, as it is possibly reason enough to move off Emotion for some folks. My understanding is that server components do work with CSS modules, for example.

Yes, Server Components work with CSS modules.

The case that I think we're eager to understand is for basic static components that do things like layout and do require styling to achieve that. Do we currently β€” and will we long term β€” have a gap where CSS Modules could work for styling these components but Emotion would not?

Yes.

However, I think that you might be overestimating the need for using styles in your Server Components. From my PoV it's just much simpler for the team as a whole to limit the CSS usage to your Client Components. This way you won't have to juggle your components when you decide to add some interactivity to those components. In other words - not using styles in your Server Components makes your app (IMHO) more resilient to refactors and needless diffs in PRs.


@itsminh99 please provide a repro case for this. I can't know what's wrong if you provide just a video.

@itsminh99 you have two problems there:

  1. you didn't use explicit <head></head> in your layout (this is required and gonna be validated by Next soon, see here)
  2. you are only creating a single Emotion cache for the whole server's lifetime - this has to be created per request

You can fix both with this patch:

diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 63f0276..4ed3345 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -9,6 +9,7 @@ type RootLayoutProps = {
 const RootLayout = (props: RootLayoutProps) => {
   return (
     <html>
+      <head></head>
       <body>
         <AppProvider>
           <MainLayout {...props} />
diff --git a/src/contexts/EmotionProvider.tsx b/src/contexts/EmotionProvider.tsx
index 7ad613d..2f9aba1 100644
--- a/src/contexts/EmotionProvider.tsx
+++ b/src/contexts/EmotionProvider.tsx
@@ -46,7 +46,9 @@ const createEmotionCache = () => {
 const clientSideEmotionCache = createEmotionCache();
 
 const EmotionProvider = (props: EmotionProviderProps) => {
-  const [{ cache, flush }] = useState(clientSideEmotionCache);
+  const [{ cache, flush }] = useState(
+    isBrowser ? clientSideEmotionCache : createEmotionCache,
+  );
 
   useServerInsertedHTML(() => {
     const names = flush();

@Andarist My repo: https://github.com/itsminh99/base-next13

Would you mind putting your example back up?

Does someone configure the ColorScheme with the rootLayout ?

Anyone has a working example of this with Chakra UI? The provided solution here does not work for me. I get

Warning: Extra attributes from the server: class
body
html
ReactDevOverlay@webpack-internal:///./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:53:9
HotReload@webpack-internal:///./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:19:39
Router@webpack-internal:///./node_modules/next/dist/client/components/app-router.js:97:89
ErrorBoundaryHandler@webpack-internal:///./node_modules/next/dist/client/components/error-boundary.js:28:9
ErrorBoundary@webpack-internal:///./node_modules/next/dist/client/components/error-boundary.js:40:56
AppRouter
ServerRoot@webpack-internal:///./node_modules/next/dist/client/app-index.js:113:25
RSCComponent
Root@webpack-internal:///./node_modules/next/dist/client/app-index.js:130:25

chakra-ui/chakra-ui#7111
chakra-ui/chakra-ui-docs#1292 (comment)

We don't put any attributes on the body so I can only assume that this is not strictly related to Emotion.

@Andarist I appreciate your response. Please disregard my previous message. I've manage to get rid of it by creating a new app using next create app. it is gone now but I am having a different issue when I implement your reply above
#2928 (comment)
The warning I get is this

Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information.
    at style

I can toggle it on and off by taking in/out the RootStyleRegistry in my main app layout file

import "./globals.css";
import Chakra from "../lib/chakraProvider";
import RootStyleRegistry from "../lib/emotion";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      {/*
        <head /> will contain the components returned by the nearest parent
        head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
      */}
      <head />
      <body>
        <RootStyleRegistry>
          <Chakra>{children}</Chakra>
        </RootStyleRegistry>
      </body>
    </html>
  );
}

// chakra provider
"use client";

import { ChakraProvider } from "@chakra-ui/react";
import { theme as proTheme } from "@chakra-ui/pro-theme"; 

export default function Chakra({ children }: { children: React.ReactNode }) {
  return <ChakraProvider theme={proTheme}>{children}</ChakraProvider>;
}


We may want to add an explicit API for this but this works today:

// app/emotion.tsx
"use client";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { useServerInsertedHTML } from "next/navigation";
import { useState } from "react";

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}) {
  const [cache] = useState(() => {
    const cache = createCache({ key: "css" });
    cache.compat = true;
    return cache;
  });

  useServerInsertedHTML(() => {
    return (
      <style
        data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(" ")}`}
        dangerouslySetInnerHTML={{
          __html: Object.values(cache.inserted).join(" "),
        }}
      />
    );
  });

  return <CacheProvider value={cache}>{children}</CacheProvider>;
}

// app/layout.tsx
import RootStyleRegistry from "./emotion";

export default function RootLayout({ children }: { children: JSX.Element }) {
  return (
    <html>
      <head></head>
      <body>
        <RootStyleRegistry>{children}</RootStyleRegistry>
      </body>
    </html>
  );
}

// app/page.tsx
/** @jsxImportSource @emotion/react */
"use client";

export default function Page() {
  return <div css={{ color: "green" }}>something</div>;
}

Have a some problem with MUI5 when I switch theme mode (light/dark) in UI ((
Dirty fix that addet useEffect with delete ssr style:

  React.useEffect(() => {
    const style = document.querySelector("[data-emotion^='css ']");
    if (style) {
      style.remove();
    }
  }, []);

We may want to add an explicit API for this but this works today:

// app/emotion.tsx
"use client";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";
import { useServerInsertedHTML } from "next/navigation";
import { useState } from "react";

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}) {
  const [cache] = useState(() => {
    const cache = createCache({ key: "css" });
    cache.compat = true;
    return cache;
  });

  useServerInsertedHTML(() => {
    return (
      <style
        data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(" ")}`}
        dangerouslySetInnerHTML={{
          __html: Object.values(cache.inserted).join(" "),
        }}
      />
    );
  });

  return <CacheProvider value={cache}>{children}</CacheProvider>;
}

// app/layout.tsx
import RootStyleRegistry from "./emotion";

export default function RootLayout({ children }: { children: JSX.Element }) {
  return (
    <html>
      <head></head>
      <body>
        <RootStyleRegistry>{children}</RootStyleRegistry>
      </body>
    </html>
  );
}

// app/page.tsx
/** @jsxImportSource @emotion/react */
"use client";

export default function Page() {
  return <div css={{ color: "green" }}>something</div>;
}

Have a some problem with MUI5 when I switch theme mode (light/dark) in UI (( Dirty fix that addet useEffect with delete ssr style:

  React.useEffect(() => {
    const style = document.querySelector("[data-emotion^='css ']");
    if (style) {
      style.remove();
    }
  }, []);

The fix here didn't really work for my project. When I tried this it ruined a lot of my styles that I needed for some reason. I instead did a different dirty fix and removed the color and background-color the RootStyleRegistry initially inserts the styles. I find it works well with my initial styles and when toggling between light and dark modes.

'use client'

import { CacheProvider } from '@emotion/react'
import createCache from '@emotion/cache'
import { useServerInsertedHTML } from 'next/navigation'
import { useState } from 'react'

export default function RootStyleRegistry({
  children,
}: {
  children: JSX.Element;
}): React.ReactElement {
  const [{ cache, flush }] = useState(() => {
    const cache = createCache({ key: 'cache-key' })
    cache.compat = true
    const prevInsert = cache.insert
    let inserted: string[] = []
    cache.insert = (...args): string | void => {
      const serialized = args[1]
      if (cache.inserted[serialized.name] === undefined) {
        inserted.push(serialized.name)
      }
      return prevInsert(...args)
    }
    const flush = (): string[] => {
      const prevInserted = inserted
      inserted = []
      return prevInserted
    }
    return { cache, flush }
  })

  useServerInsertedHTML(() => {
    const names = flush()
    if (names.length === 0) return null
    let styles = ''
    for (const name of names) {
      let style = cache.inserted[name]
      const removeThemeColors = typeof style === 'string' && style.indexOf('html{') === 0
      if (removeThemeColors) style = (style as string)
        .replace(/(body{[^}]*)(background-color:[^;]*;)/i, '$1')
        .replace(/(body{[^}]*)(color:[^;]*;)/i, '$1')
      styles += style
    }

    return (
      <style
        data-emotion={`${cache.key} test ${names.join(' ')}`}
        dangerouslySetInnerHTML={{
          __html: styles,
        }}
      />
    )
  })

  return <CacheProvider value={cache}>{children}</CacheProvider>
}

I attempted to use this hack and while it works I find it causes intermittent "SSR differs from Client render" errors which made development less than fun. :/

Hey,
If you want to abstract away all these shenanigans and just get Emotion working in AppDir, you can use the TSS's <NextAppDirEmotionCacheProvider /> it's just a wrapper for code that has been shared here though.

Hi @deadlyicon, about the random hydration error, I've opened an issue about it on Versel's repo.
Hope it will be fixed soon 🀞🏻

Could someone gently tell me if my thoughts are right?

  • CSS-in-jS does already work with server components if a new provider is added as a styles colector that injects styles into head element.
  • Reactivity does not currently work with server side components. That's because, in order to rehidratate new styles, a refetch would be needed to generate new styles. If that said is right then I dont understand why is that a probem, I find this as a posible solution (not optimal).

By my slim knowledge about this topic I understand that state is needed to use MUI, Styled Components or any other CSS-in-JS libraries, so client components would be needed to ose those libraries. In that case what is the reason to add a styles collector provider as client side component if all components that use those libraries would be already client sided.

If as far as I am going everything is right and clear then I could infer that CSS-in-JS will never be posible in SSR because of it's stateless capabilities. And refetching would not be a great option because of the fetch delay.

It would be very useful to know if my thoughts are well aligned and I think that by explaining all in a single answer the above concerns would make the undertandings about this problem more strong and centralized

Could we at least know if this is something on the Emotion team's roadmap? For those looking to start projects that utilize server components, it's pretty limiting to have your entire UI framework be unable to be carried over with that new philosophy. Not passing critique or concern - just merely wanting to know if the Emotion team intends to do anything about this or if it's a more passing/distant concern. ☺️

It's on my mind to provide better out-of-the-box options to integrate with both Next.js and other frameworks. However, when it comes to Next.js - you already can almost fully integrate using the snippets shared in this thread.

Hi @Andarist,
I'm pretty sure @cereallarceny is asking about being able to use emotion in server components.
A previous answer of yours mentioned that:

Server Components were designed in a way that is at fundamental odds with CSS-in-JS. So don't expect Emotion (or other popular CSS-in-JS libs) to introduce support for them. They just don't allow us to integrate with them anyhow.

Unless there is some new development, I think this is the answer people are looking for unfortunately... πŸ˜•

That sounds like the writing is on the wall to me. Totally understand the reasoning, thanks for the response.

We know that Emotion will not work exactly the same in server components, but it is rather a stretch that, "the writing is on the wall". The /app directory with Next.js is not production ready, and afaik there is not another popular React framework offering server components at all. How Emotion might interface with server components is going to be based on how people use them, which is thus unknown.

There are already a few ideas in this thread about how CSS styles might be applied to server components, either by wrapping with a client component which styles named classes, or somehow offering a static output of CSS styles for the server component. This would be more consistent with the current Emotion best practices of avoiding dynamic styles anyways. How much this breaks things will depend a lot on how much people put into server components. Client components will still exist and work fine.

@paluchi Server components and SSR are totally different things. Emotion supports SSR just fine.

What would be interesting is if Emotion could offer a subset of its features in server components (features that would not require context and all other client component technologies, similar to the framework-agnostic @emotion/css), allowing for styling of eg. Layout components without using either client components or a different styling solution. Switching components that you want to style to client components seems like a pretty big drawback. React Server Components will come to other frameworks as well, so it would be good to support this.

In our team, even though we're huge supporters of Emotion and CSS-in-JS, we have made the difficult decision to switch back to CSS Modules because of lack of support for styling in Server Components.

@Andarist Thinking more about my comment above, do you think it would be a temporary workaround to recommend @emotion/css to React Server Components users who want to avoid client components for styling? Or generally, what are your thoughts on my suggestions, for users who absolutely do not want to switch to client components for styling?

@karlhorky Are you using server components in production?

The main problem with CSS-in-JS styles on the server is not that they need React context. In this context, React context is just a "state" (don't confuse it with React state). It's a "registry" of generated styles and stuff like that. So @emotion/css doesn't solve anything here - it's still impossible with it to reliably "transfer" styles from the server to the client without putting all the "current"* styles in the payload. You don't want to send all the "current" styles in the payload because those styles could have already been sent to the client in previous requests or they could have already been generated on the client. This would be a huge waste of network traffic.

*current as in - the ones used by what is currently fetched from the server, this could be a whole document, a page, a nested layout, or just a single component. Remember that server components are not only about fetching the initial document but they also support "refetching" things.

You don't want to send all the "current" styles in the payload because those styles could have already been sent to the client in previous requests...

This would be a huge waste of network traffic

Ok, I think I understand the optimization that Emotion is attempting to do here.

I guess what I'm asking is: is there a different way that Emotion could offer a subset of its features in React Server Components without downsides like this. Eg. wonder how CSS Modules does it - I'm assuming that they are not re-transferring all of the styles every time (eg. maybe they are generating a static CSS file for use with a <link> tag?)

@karlhorky CSS modules compile to a CSS file with generated unique classnames and return an object mapping friendly classnames to the unique ones. They send the styles all at once upfront.

pigoz commented

@karlhorky If you like emotion because you can mix in typescript variables but CSS modules is enough for your use case you might want to look at vanilla extract

Vanilla Extract looks cool! But it does not support normal CSS in strings (they only have object syntax) which is a dealbreaker for us, because we teach CSS to beginners.

Seems like there is a discussion to possibly include support for CSS strings in Vanilla Extract, I will watch over there.

@karlhorky Sounds like you want to use linaria

@Andarist You wrote:

You don't want to send all the "current" styles in the payload because those styles could have already been sent to the client in previous requests or they could have already been generated on the client. This would be a huge waste of network traffic.

Might be possible to split the server styles and client styles? This would reduce the 'overfetching' of the styles to only be the server styles? That doesn't sound so bad, when refreshing the server components it's styles should be refreshed as well in that case?

In any case, this isn't some big amount of kB's, is it? This probably beats moving the styles to client components in terms of perf. Especially for the first paint, as it doesn't need to render those client components? For refetching it it only needs to download, doesn't need to execute much, which trades kB's for CPU?

Is there a way to override this new SSR-oriented behavior ? I'm stuck between a rock and a hard place at the moment:
I'm really fond of the new layout system, but my whole app is based on a design system that relies on Chakra UI, which relies on emotion, and I don't want to prefix every single file that includes one of these components with a 'use client';

On a non emotion-related level, I'm also using other providers which seem to conflict with the new appDir (SWR and react-intl), which makes me wonder: should I keep my current version of the app, and ditch this new wonderful layout system, or should I just turn almost 99% of my app into client-side components ?

Might be possible to split the server styles and client styles? This would reduce the 'overfetching' of the styles to only be the server styles?

It would reduce it only to server styles. It would raise the complexity level on the client side because it still has to do something with the received styles, it might have to hydrate them into the cache, it might have to move them in the DOM, and more. My current PoV is that when it comes to styles in server components it's mostly YAGNI. We are mostly discussing this in a vacuum right now anyway. I think that it's best to have this constraint in place right now and let people use this in production. Once they do it we will actually get informed feedback rather than a theoretical one.

Especially for the first paint, as it doesn't need to render those client components?

Client components have to very much render for the first paint - or at least, they would render in the majority of use cases. Remember that you can SSR render client components - you just can't "refetch" them.

I don't want to prefix every single file that includes one of these components with a 'use client';

But you don't have to do that. It's optional and will probably be done within Chakra, Emotion, etc. You don't necessarily have to leak this into your app code.

My current PoV is that when it comes to styles in server components it's mostly YAGNI. We are mostly discussing this in a vacuum right now anyway. I think that it's best to have this constraint in place right now and let people use this in production.

Not to make this into a heated discussion or anything, but that is what my feedback in my comments above is: feedback from our team from using it in production (and also trying to develop a teaching curriculum around it).

I want to have a Layout component (eg. RootLayout) that is a Server Component, and have my styling for that layout also stay in Server Components (potentially even in the same Layout file). I only want to switch to a Client Component if there is some client-only features I'm using such as useState. I think this would not be an uncommon wish from others.

This also aligns with the current guidance from the Next.js team to just leave everything a server component until you absolutely need a client component.

Hm, maybe we'll have to think about smth like useServerStyles (or something else) then. The refetching story is quite fuzzy to me and I'm not sure how to "plug" into it anything - so far I was only investigating the rendering of the initial document.

I feel like that should probably be a separate discussion - with its own thread. Let's keep this thread about "basic" /app directory support.

Thanks for the consideration. Sounds reasonable to move it πŸ‘ I've opened #2978 for Emotion + React Server Components.

My current PoV is that when it comes to styles in server components it's mostly YAGNI. We are mostly discussing this in a vacuum right now anyway. I think that it's best to have this constraint in place right now and let people use this in production.

I'm currently porting a production site (server rendered by PlayFramework) into a NextJS 13 app with app dir + MUI component library (using Emotion).

For our existing site:

  • server-rendered html is critical (for seo reasons)
  • pages are dynamically built at request time from a graphql query (which fetches all of the data needed by the page) which is then rendered into html.
  • "client-side functionality" is sprinkled in using Vue components which are rendered as part of the server html and then initialized in the browser.
  • we use scss and bundle all the styles used by a page into a single <style> element that is injected into the <head> at render time

The site is highly responsive to viewport sizing.
What I find really helpful/useful about MUI + Emotion is the support for responsive css properties in js.
So for the new NextJS site, I've configured all our breakpoints in the MUI theme like so:

breakpoints: {
  values: {
    bp0: 0,
    bp1: 375, // iphone 5 landscape & iphone 6/7/8/X/X+ portrait
    bp2: 414, // iphone 7+/8+ portrait
    bp3: 667, // iphone 6/7/7+/8/8+ landscape
    bp4: 768, // ipad portrait & iphone X/X+ landscape
    bp5: 1024, // ipad landscape & ipad pro portrait
    bp6: 1366, // ipad pro landscape
  },
}

And then reference those breakpoints in MUI components like so:

<Box
  component="aside"
  display={{
    bp0: 'none',
    bp4: 'block', // visible on screens 768+ css pixels wide
  }}>
  <Box
    component="section"
    display="flex"
    flexDirection={{
      bp4: 'column',
      bp6: 'row',
    }}>
    <div>column on medium sized screens</div>
    <div>row on larger screens</div>
  </Box>
</Box>

I would use css modules but media queries don't support css-variables and the browser env() spec doesn't support user-defined values yet.
This means my css modules would have the literal breakpoint values littered all over...

As @karlhorky mentioned above, I'd like to reserve client components for things that actually require client side only functionality (local storage, event handlers, use state hook, effect hooks, etc).

Right now, my workaround is to mark virtually all "layout" components with 'use client' while I wait/pray/hope that I'll eventually be able to remove it.

@bpossolo

server-rendered html is critical (for seo reasons)

SSR is working fine, adding "use client"; isn't equivalent to using < NoSsr />.
Server components are components that render only on the server.
Classic components are components that renders on the server and the client.
The goal of server component is to reduce the size of JS shipped to the client by keeping component code only on the server side. I.e., the rendering happens and only happens on the server side, even if the loading of the component is triggered on the client side (via client-side routing).. It also makes life easyer by enabling to write async code in the comoponents body.

@garronej yah, as I mentioned, the majority of our pages are comprised of components which only need to be rendered on the server.. i.e. we don't need/want all the associated js being shipped to the client when it's purely html + css + media queries that don't change after the initial rendering... ergo, I don't want to mark everything as 'use client'. and yes, I'm also using async/await in the server pages for retrieving the graphql data.

@garronej yah, as I mentioned, the majority of our pages are comprised of components which only need to be rendered on the server.. i.e. we don't need/want all the associated js being shipped to the client when it's purely html + css + media queries that don't change after the initial rendering... ergo, I don't want to mark everything as 'use client'. and yes, I'm also using async/await in the server pages for retrieving the graphql data.

Unfortunately, hydration is crucial to CSS-in-JS and I doubt they can move away from it any time soon. It takes great effort to do so.

To be honest, you already made a bad decision of trying to use an unstable feature in a company project. And further making another bad decision of not reading the Next.js document carefully on what is currently supported and what's not. And it is to be expected that early adopters have a few hiccups here and there.

You have nothing to lose from using use client in terms of performance. It is basically equivalent to using /pages. The most problematic thing is properly data fetching, where you have to create a server component wrapper for every place you need to fetch data because MUI components can only be client components.

Also, you properly constantly facing this:

import ServerComponent from './ServerComponent';

export default function ClientComponent() {
  return (
    <SomeMuiComponent>
      <ServerComponent />
    </SomeMuiComponent>
  );
}

To be honest, you already made a bad decision of trying to use an unstable feature in a company project. And further making another bad decision of not reading the Next.js document carefully on what is currently supported and what's not. And it is to be expected that early adopters have a few hiccups here and there.

@mwskwong I'm not sure what the point of your pretty condescending comment is but you're making all sorts of assumptions that are just incorrect...
the devs asked for examples of real world use-cases for /app + server components + emotion so @karlhorky and I gave examples.
also, I haven't had to wrap any components like your example so I have no idea what you're talking about...
it sounds like you have little-to-no faith in the /app dir feature + server components (which for the most part work really well) so I'm not sure why you're chiming in here.

To be honest, you already made a bad decision of trying to use an unstable feature in a company project. And further making another bad decision of not reading the Next.js document carefully on what is currently supported and what's not. And it is to be expected that early adopters have a few hiccups here and there.

@mwskwong I'm not sure what the point of your pretty condescending comment is but you're making all sorts of assumptions that are just incorrect... the devs asked for examples of real world use-cases for /app + server components + emotion so @karlhorky and I gave examples. also, I haven't had to wrap any components like your example so I have no idea what you're talking about... it sounds like you have little-to-no faith in the /app dir feature + server components (which for the most part work really well) so I'm not sure why you're chiming in here.

Then perhaps I misunderstood, my apologies. Not trying to be a dick here. That's what happens when joining a discussion without context and making up my mind too quickly. But TBH, if I just look at the conversations on this topic alone, it does sound like you are trying to migrate a production site to the next 13 app/ dir for real.

As for you mentioning that you never faced the situation of wrapping server components in client components, I'm actually pretty surprised.

Are you only doing data fetching at the page level? Because if you do (some) data fetching at the component level, then you have to make the corresponding component a server component (properly by wrapping it in a server component wrapper), and may need to import it into a client component since you are using MUI. (Yes, I know a workaround is available, but that just makes the structure weird).

Update: Turns out it's useSearchParams hook causing tags never to show up, see vercel/next.js#44868. Replacing with a custom hook watching popstate instead in a useEffect. Still, the principles here for blending client and server components is still relevant.


A couple of additional issues I've run across trying to work around this (thanks to #2928 (comment) so much!)

Primarily this came out of the discovery that if your layout.tsx is a client only component, none of the Next server-side head will render – meaning any meta tags like title, description, or opengraph properties will not be available to crawlers or unfurlers. Likely related issue: vercel/next.js#42268

To fix this, and a few other issues with theme loading, I tried moving client-only code into separate components, which is recommended in the docs.

I ended up with a hybrid server-side root component, so Next will handle conditionally rendering client leaves.

In my case, I had to do some extra legwork. This is probably a better approach, but unfortunately it still doesn't seem to get the tags to load server-side πŸ˜”.

  1. If you're creating a custom theme and exporting it, that theme needs a use client directive at the top:

// styles/mainTheme.ts

'use client'; 
// ^ must have

import { createTheme, responsiveFontSizes, Theme } from "@mui/material/styles";

const theme: Theme = createTheme({
  // ... your theme here
});

const responsive = responsiveFontSizes(theme), {
  factor: 1.25,
});

export default responsive;
  1. If you're using <ThemeProvider />, you have to create a lightweight client wrapper around it

// lib/SSR/theme.provider.tsx

"use client";

import { ThemeProvider } from "@mui/material";
import { DefaultTheme, ThemeProviderProps } from "@mui/styles";
import React from "react";

export const SSRThemeProvider = <T = DefaultTheme,>(
  props: ThemeProviderProps<T>
): React.ReactElement<ThemeProviderProps<T>> => {
  return (
    <React.StrictMode>
      <ThemeProvider {...props} />
    </React.StrictMode>
  );
};
  1. Your client-only content must have the use-client directive and wrap your content in the RootStyleRegistry.

// content/home.tsx

"use client";

import { CssBaseline } from "@mui/material";
import RootStyleRegistry from "lib/SSR"; // this is the emotion file from the linked post in this thread, I moved it

export const HomePage = () => {
  
  // do some cool client side stuff here like useEffect or useState like a boss

  return (
    <>
      <CssBaseline key="css-baseline" />
      <RootStyleRegistry>
      <body>
        <main>
          // your content here
        </main>
      </body>
    </>
  );
};

Then your page.tsx, layout.tsx, and head.tsx will look like:

// head.tsx

export default function Head() {
  return (
     <>
       <title>Your page title</title>
       // other meta tags
     </>
   );
}

// layout.tsx

import { SSRThemeProvider } from "lib/SSR/theme";
import React from "react";
import theme from "styles/mainTheme";

// This layout is now can handle both server and client rendering
// and in particular, will render <head> tags on the server.

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <React.StrictMode>
      <html lang="en" key="root">
        <head />
      </html>
      {/* ^ duplicate your HTML here with an empty body (or fallback plain text) and make sure the key matches. */}
      <SSRThemeProvider theme={theme} key="theme-provider">
        <html lang="en" key="root">
          {/*
            <head /> will contain the components returned by the nearest parent
            head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
            */}
          <head />
          <body>{children}</body>
        </html>
      </SSRThemeProvider>
    </React.StrictMode>
  );
};

// home.tsx

// This page is now can handle both server and client rendering

import { HomePage } from "content/home";

export default async function HybridHome() {
  return <HomePage />;
};

@karlhorky Sounds like you want to use linaria

Yep, Linaria also seems like another nice alternative to Emotion, which offers some of the functionality of Emotion's CSS-in-JS without the downsides of switching to Client Components.

However up until recently, the example / plugin was not updated for the Next.js 13 app directory, now it seems there is a solution for this:

Has anyone encountered an issue with switching the light/dark theme?
Some parts seem to not update (I'm using MUI)

Has anyone encountered an issue with switching the light/dark theme? Some parts seem to not update (I'm using MUI)

Yes, there seems to be a hydration error when creating the theme based on selected mode (light/dark).

image

Has anyone encountered an issue with switching the light/dark theme? Some parts seem to not update (I'm using MUI)

Yes, there seems to be a hydration error when creating the theme based on selected mode (light/dark).

image

Yes, I'm experiencing this too

I'm currently forced to do a full-page reload to be able to switch between light/dark mode.

@MartinXPN I've also did that using cookies as state for the selected theme. Read from cookies the value on SSR, then pass it to the client to build the theme. To switch it, update the cookies on client side (js-cookies) and reload the page. This is a nasty hack, but until we have a stable solution this does the job.

Next's App Router is officially stable. Would love to know progress on Emotion's efforts to support Server Components and Next's App directory.

Managed to get decent support for appDir using the suggestions in this thread, but every time a fast reload takes place it flickers in/out the Global Styles. Additionally, this earning keeps popping up on every route build.

image

Which is just a fragment that inserts CSSBaseline and GlobalStyles from MUI into the ThemeProvider.

image

Anybody else having this issue?

Next's App Router is officially stable. Would love to know progress on Emotion's efforts to support Server Components and Next's App directory.

@Andarist please... pretty please :)

Hi, Team emotion. I'm not sure if you guys will watch this,
Recently, nextJS13 is finally released stable version which is 13.4.1. So Our FE team is trying to use it with our own made common components styled by emotion(@emotion/styled v11.10.5, @emotion Slack Inviter/react v11.10.5). But we got an error which is the photo. As you know, next13 has a lot of changes like separate client and server component. But as you can see the error, emotion styled or css tags only use client component they say. I think all of developers around world need to use both side.
I’m sure you guys know this problem and currently working on support NextJS latest version. So can you tell me when developing for supporting will be done and release?
Thank you for reading

image

Can someone explain to me, why when I'm going to use a css-in-js library, I need to do the configuration using the registry.tsx file (for example styled components or emotion) in the app folder ? And why is it not just install and use like in other versions of next and in the component that I need to style I put 'use client' ?

@gabrielleacuba I'll answer the second question. The app directory is not only about another way to structure code. The issue (feature) is that it enables React Server Components (note: this is not a distinction between SSR and client, this is a new concept). Every component imported into the app directory is treated as a server component by default and needs to be marked with "use client" if it needs to run on the client (e.g. when it uses hooks). The Emotion styling depends on the theme in the context provided by ThemeProvider and as of the error above, context can't be used on the server, it's a client thing.
Emotion will probably need to rethink and rework the approach in order to support the server components out-of-the-box.

@MoZZANG I was able to resolve this issue by doing the following:

  1. Isolating the providers for my app to a separate client component as described here
  2. Removing "jsxImportSource": "@emotion/react" from my .tsconfig, and setting it to "react".

Hope this helps :)

Would be nice if Material UI website provided like a warning at the installation page for new people trying to use the latest Nextjs /app dir with Material UI, along with maybe a link to a timeline for the people / companies who already use Material UI and are considering migrating to the /app dir.

I know that Nextjs has it in it's documentation at css-in-js as "not currently supported and working on support" but it's way below and most newbies wouldn't know to look for it first. And them trying out MUI with app/ will lead to a lot of frustration and would leave a bad taste and poor first impression about MUI (when it's not really MUI's fault).

Also, about the "link" to a timeline / expected date of support, I know that there are these github threads and I (and most other people) am keeping track of them from time to time. But they are too long and started from Oct 2022 (when /app was in beta) and has hundreds of comments to "look for the right solution / answer". Again especially for newbies or people just looking into the issue for the first time and considering migrating to the /app since it's now stable.

Those two simple things (warning & timeline link) in mui website installation page would save a lot of frustration and time for a lot of people.

Also, thanks for your hard work, I know it must have been frustrating for you people too. I hope people at Next js had already considered the effects of their changes and would have devised a solution or at least a roadmap to a solution for libraries like MUI and emotion to help ease the transition. :)

I use MUI with the app directory and it works just fine.
I need to mark components that directly use an mui component with β€œuse client” but everything works.
I’m not sure what all the fuss is about.

I’m not sure what all the fuss is about.

@bpossolo
Because using "use client" would be merely a temporary makeshift solution and "emotion" wouldn't be able to benefit from React Server Components.

@takecchi yes there is the downside that it sends superfluous js to the browser but there’s nothing stopping a person from using mui with the app directory right now as many are implying. That’s what I mean when I say I don’t know what the fuss is all about.

@bpossolo I see. I think this discussion has become too lengthy and has strayed off topic

@takecchi yes there is the downside that it sends superfluous js to the browser but there’s nothing stopping a person from using mui with the app directory right now as many are implying. That’s what I mean when I say I don’t know what the fuss is all about.

Another thing is since every MUI component has to be a client component, it makes it practically impossible to do server-side data fetching on the component level.

Can anyone leave just plain answer: When will it work? It's main topic and link from every place, and it's flooded. For now server components won't work with 'use client' because of db access and that mad cache 'context' will break them as client components, even if not used. It makes all emotion based frameworks useless. So it's pretty important to know what the status, and what is going on. For now answer is: move to tailwind and forget.

As a long-time user and big fan of Emotion, I’m eagerly awaiting Next.js v13.4 support.

While we’re waiting for the official support, I’ve been working on my personal project which may serve as a temporary alternative. It provides similar APIs (such as styled and css) to Emotion and avoids the current Next.js App Router limitation by handling style extraction at compile-time.

Though it might not be the perfect solution for everyone, I thought I’d share it here in case anyone finds it useful in this interim period.

@poteboy Your library doesn't support props and you should clearly state it before recommending it to anyone already using emotion, so no one will feel discomfort after spending some time reading the docs πŸ€“

@robertpiosik Thanks for taking the time to check out the doc, and sorry if it ended up disappointing you.

Just to give a little context, RSCs are essentially static - they get rendered on the server, then sent off to the browser as HTML. This means they can't be updated using JavaScript on the client side, which includes any DOM manipulations that Emotion operates under the hood for dynamic props. At this stage, we're unable to pull that off without some support from Vercel. So, that's why I referred to Kuma (my library) as a 'temporary alternative' :)

However, if your props are static and don't need to be changed after they're rendered, you can totally use them like this in Kuma:

const Box = styled('div')``
<Box fontSize={24} color='red' />

Hope this clears things up! :)

@robertpiosik Thanks for taking the time to check out the doc, and sorry if it ended up disappointing you.

Just to give a little context, RSCs are essentially static - they get rendered on the server, then sent off to the browser as HTML. This means they can't be updated using JavaScript on the client side, which includes any DOM manipulations that Emotion operates under the hood for dynamic props. At this stage, we're unable to pull that off without some support from Vercel. So, that's why I referred to Kuma (my library) as a 'temporary alternative' :)

However, if your props are static and don't need to be changed after they're rendered, you can totally use them like this in Kuma:

const Box = styled('div')``
<Box fontSize={24} color='red' />

Hope this clears things up! :)

Thank you for offering this solution! I disagree with Robert's aggressive tone, but they are right to some extent. I, as a builder and also consumer of UI component libraries, don't see how I should use Kuma to create components that are customizable since dynamic props cannot be used. I've checked the code of your library and I fully understand why dynamic props aren't available. Did you find any solution? For me it seems that I have to create a different styled component (using @kuma-ui/system) for each possible component variant.

@a-saven I have been following this issue for a while, and the plain answer is nothing is coming soon. Tailwind is probably what you should have always been using if the performance claims of the /app dir and server components feel essential. Otherwise, there is actually zero downside to using client components.

The /pages directory is still supported and works fine in Next. Also, you can simulate the get...Props data fetching methods with the /app directory by querying the DB in server components and passing the results to client components as props.

@a-saven I have been following this issue for a while, and the plain answer is nothing is coming soon. Tailwind is probably what you should have always been using if the performance claims of the /app dir and server components feel essential. Otherwise, there is actually zero downside to using client components.

The /pages directory is still supported and works fine in Next. Also, you can simulate the get...Props data fetching methods with the /app directory by querying the DB in server components and passing the results to client components as props.

I know its slightly off topic here, but I think the page + client components isn't actually that bad structurally. It adds a level of demarcation.