You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A few months ago, I migrated my portfolio from React to Next.
However, errors occurred in most components using Emotion. Interestingly, the errors disappeared when I added 'use client', but eventually I ended up having to add 'use client' to all components. This defeated the purpose of using Next. So I abandoned Emotion and switched to Vanilla-Extract.
Why does Emotion throw errors in Next without 'use client'? I believe this problem stems from the delivery mechanism.
Background Knowledge (Brief)
Drawbacks of SSR (Server Side Rendering)
The biggest problem with SSR (Server Side Rendering) before Next.js 13 was that components were called twice on both server and client. Even if the server executes React components to create and send HTML, the browser must go through a hydration process to connect events and state to the existing DOM by calling the components after receiving the HTML. Ultimately, components executed on the server are re-executed on the client. This means they are included in the JavaScript bundle.
What About RSC
RSC separated the component execution environment into server and client. Server components are only called on the server, sending lightweight RSC Payload and HTML, while client components go through the same hydration process as before.
That's why even with 'use client', SSR (Server Side Rendering) is attempted by default.
Emotion with SSR
First, let's look at how Emotion could run in an SSR environment.
The server calls the top-level component <App /> with renderToString.
consthtml=renderToString(<App/>);// html = '<button class="css-abc123">Click</button>'
During renderToString, whenever a styled component is evaluated, Emotion generates styles and registers them in the cache.
collectedStyles={'css-abc123': 'color: red;'}
Once the HTML is complete, extractCritical(html) extracts the CSS actually being used. At this point, the html is modified and returned to ensure the HTML created on the server matches the Emotion result that will be re-executed on the client as closely as possible.
On the client side, the entire JS bundle including the Emotion library is also downloaded. At this point, hydrate(ids) is called to pass the ids extracted from the server directly to Emotion. This is to inject styles already inserted on the server into the cache to prevent duplicate insertion.
import{hydrate}from'@emotion/css'hydrate(ids)
In summary, in SSR, static styling is already included in the <style> tag in <head>. When dynamic styling is needed via JS, the Emotion engine included in the JavaScript bundle executes it.
RSC Payload
In RSC, server components only run on the server, and RSC Payload is created based on this execution result. RSC Payload contains the following information:
Page information and expected output
Fallback to show during <Suspense> state
All props passed from server components to client components
The client interprets this Payload, merges it with the existing React Tree, and updates the necessary DOM. However, on the first page, since React hasn't been initialized yet, HTML is generated and sent together. After that, when we do things like navigation, only RSC Payload is received.
Style information is not included in RSC Payload. RSC Payload only contains serialized structural data to reconstruct the React tree. So even if Emotion collects styles when components are executed on the server, there's no space to include and send them. Additionally, the JavaScript bundle of server components is not delivered to the client, and they don't go through the hydration process.
GitHub Issue Discussions at the Time
Looking at GitHub issues, MUI was using Emotion as its internal styling engine at the time. The Emotion team said that Next.js rushed the version 13 update without properly communicating with library authors, and that Styled-Components was in a similar situation.
Next.js rushed the release of their docs without consulting library authors. The mentioned Styled-Components "support" looks almost exactly the same as the Emotion support can look like (see the comment here). There is no special API in SC that integrates with RSC in any special way.
The code that the Emotion team posted as a workaround for using Root Layout as a server component was just a workaround. Only initial styles are applied, and the rest of the styling must be used with 'use client'.
We may want to add an explicit API for this but this works today:
// app/emotion.tsx"use client";import{CacheProvider}from"@emotion/react";importcreateCachefrom"@emotion/cache";import{useServerInsertedHTML}from"next/navigation";import{useState}from"react";exportdefaultfunctionRootStyleRegistry({
children,}: {children: JSX.Element;}){// ... (cache logic)return<CacheProvidervalue={cache}>{children}</CacheProvider>;}// app/layout.tsximportRootStyleRegistryfrom"./emotion";// 'use client' is not added to layout, but ultimately 'use client' is added everywhere elseexportdefaultfunctionRootLayout({ children }: {children: JSX.Element}){return(<html><head></head><body><RootStyleRegistry>{children}</RootStyleRegistry></body></html>);}// app/page.tsx/** @jsxImportSource@emotion/react */"use client";exportdefaultfunctionPage(){return<divcss={{color: "green"}}>something</div>;}
Conclusion
While researching how vanilla-extract works at my company, I became curious about 'Why can't we use Emotion in Next?' and decided to write this post.
At the time, I just thought 'Sigh, I guess Emotion isn't compatible with Next' and moved on, but this time I learned a lot by continuously thinking about it and researching. I also learned about RSC Payload for the first time, and now I understand why when navigating pages, the network tab only shows files like _rsc=1r34m being fetched.
Just wanted to share this with you all!
What do you guys think?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
A few months ago, I migrated my portfolio from React to Next.
However, errors occurred in most components using Emotion. Interestingly, the errors disappeared when I added
'use client', but eventually I ended up having to add'use client'to all components. This defeated the purpose of using Next. So I abandoned Emotion and switched to Vanilla-Extract.Why does Emotion throw errors in Next without
'use client'? I believe this problem stems from the delivery mechanism.Background Knowledge (Brief)
Drawbacks of SSR (Server Side Rendering)
The biggest problem with SSR (Server Side Rendering) before Next.js 13 was that components were called twice on both server and client. Even if the server executes React components to create and send HTML, the browser must go through a hydration process to connect events and state to the existing DOM by calling the components after receiving the HTML. Ultimately, components executed on the server are re-executed on the client. This means they are included in the JavaScript bundle.
What About RSC
RSC separated the component execution environment into server and client. Server components are only called on the server, sending lightweight RSC Payload and HTML, while client components go through the same hydration process as before.
Emotion with SSR
First, let's look at how Emotion could run in an SSR environment.
<App />with renderToString.extractCritical(html)extracts the CSS actually being used. At this point, the html is modified and returned to ensure the HTML created on the server matches the Emotion result that will be re-executed on the client as closely as possible.hydrate(ids)is called to pass the ids extracted from the server directly to Emotion. This is to inject styles already inserted on the server into the cache to prevent duplicate insertion.In summary, in SSR, static styling is already included in the
<style>tag in<head>. When dynamic styling is needed via JS, the Emotion engine included in the JavaScript bundle executes it.RSC Payload
In RSC, server components only run on the server, and RSC Payload is created based on this execution result. RSC Payload contains the following information:
<Suspense>stateThe client interprets this Payload, merges it with the existing React Tree, and updates the necessary DOM. However, on the first page, since React hasn't been initialized yet, HTML is generated and sent together. After that, when we do things like navigation, only RSC Payload is received.
Style information is not included in RSC Payload. RSC Payload only contains serialized structural data to reconstruct the React tree. So even if Emotion collects styles when components are executed on the server, there's no space to include and send them. Additionally, the JavaScript bundle of server components is not delivered to the client, and they don't go through the hydration process.
GitHub Issue Discussions at the Time
Looking at GitHub issues, MUI was using Emotion as its internal styling engine at the time. The Emotion team said that Next.js rushed the version 13 update without properly communicating with library authors, and that Styled-Components was in a similar situation.
The code that the Emotion team posted as a workaround for using Root Layout as a server component was just a workaround. Only initial styles are applied, and the rest of the styling must be used with
'use client'.#2928 (comment)
Conclusion
While researching how vanilla-extract works at my company, I became curious about 'Why can't we use Emotion in Next?' and decided to write this post.
At the time, I just thought 'Sigh, I guess Emotion isn't compatible with Next' and moved on, but this time I learned a lot by continuously thinking about it and researching. I also learned about RSC Payload for the first time, and now I understand why when navigating pages, the network tab only shows files like
_rsc=1r34mbeing fetched.Just wanted to share this with you all!
What do you guys think?
References
https://emotion.sh/docs/ssr#extractcritical
#2928 (comment)
#2978
https://nextjs.org/learn/dashboard-app/streaming
https://roy-jung.github.io/250323-react-server-components/
All reactions