@@ -47,3 +47,244 @@ import App from './App';
4747
4848const html = renderStylesToString (renderToString (< App / > ));
4949```
50+
51+ ## SSR Compatibility
52+
53+ Emotion generates styles at runtime and injects them into the DOM. For server-side rendering, styles must be extracted during rendering and inserted into the HTML before it's sent to the client. Without proper configuration, you'll see a flash of unstyled content (FOUC).
54+
55+ > ⚠️ ** Important:**
56+ >
57+ > - Emotion does not [ currently support React Server Components] ( https://github.com/emotion-js/emotion/issues/2978 ) . You must use ` 'use client' ` directive in Next.js.
58+ > - Ensure you're using the latest version of any ` emotion ` packages alongside this package.
59+ > - LeafyGreen UI components may require additional configuration beyond what's documented here.
60+
61+ ### Framework Guides
62+
63+ - [ Next.js (App Router)] ( #nextjs-app-router )
64+ - [ Next.js (Pages Router)] ( #nextjs-pages-router )
65+ - [ React Router v7+] ( #react-router-v7 )
66+ - [ Gatsby.js] ( #gatsbyjs )
67+
68+ ---
69+
70+ ### Next.js (App Router)
71+
72+ #### 1. Create the Emotion Registry
73+
74+ Create a new file at ` src/app/EmotionRegistry.tsx ` :
75+
76+ ``` jsx
77+ ' use client' ;
78+
79+ import { useServerInsertedHTML } from ' next/navigation' ;
80+ import { cache , CacheProvider } from ' @leafygreen-ui/emotion' ;
81+
82+ export default function EmotionRegistry ({
83+ children,
84+ }: {
85+ children: React .ReactNode ,
86+ }) {
87+ useServerInsertedHTML (() => {
88+ const names = Object .keys (cache .inserted );
89+ if (names .length === 0 ) return null ;
90+
91+ let styles = ' ' ;
92+ for (const name of names) {
93+ const style = cache .inserted [name];
94+ if (typeof style === ' string' ) {
95+ styles += style;
96+ }
97+ }
98+
99+ return (
100+ < style
101+ data- emotion= {` ${ cache .key } ${ names .join (' ' )} ` }
102+ dangerouslySetInnerHTML= {{ __html: styles }}
103+ / >
104+ );
105+ });
106+
107+ return < CacheProvider value= {cache}> {children}< / CacheProvider> ;
108+ }
109+ ```
110+
111+ #### 2. Add the Registry to Your Root Layout
112+
113+ Wrap your application in ` src/app/layout.tsx ` :
114+
115+ ``` tsx
116+ import type { Metadata } from ' next' ;
117+ import EmotionRegistry from ' ./EmotionRegistry' ;
118+
119+ export const metadata: Metadata = {
120+ title: ' My App' ,
121+ description: ' My application description' ,
122+ };
123+
124+ export default function RootLayout({
125+ children ,
126+ }: Readonly <{
127+ children: React .ReactNode ;
128+ }>) {
129+ return (
130+ <html lang = " en" >
131+ <body >
132+ <EmotionRegistry >{ children } </EmotionRegistry >
133+ </body >
134+ </html >
135+ );
136+ }
137+ ```
138+
139+ #### 3. Use in Client Components
140+
141+ > The ` css ` function only works in ** Client Components** :
142+
143+ ``` tsx
144+ ' use client' ;
145+
146+ import { css } from ' @leafygreen-ui/emotion' ;
147+
148+ export default function MyComponent() {
149+ return (
150+ <h1
151+ className = { css `
152+ color: red;
153+ ` }
154+ >
155+ Hello World
156+ </h1 >
157+ );
158+ }
159+ ```
160+
161+ ---
162+
163+ ### Next.js (Pages Router)
164+
165+ Add Emotion's critical CSS extraction to your ` _document ` file:
166+
167+ ``` tsx
168+ import { extractCritical } from ' @leafygreen-ui/emotion' ;
169+
170+ export default class AppDocument extends Document {
171+ static async getInitialProps(
172+ ctx : DocumentContext ,
173+ ): Promise <DocumentInitialProps > {
174+ const initialProps = await Document .getInitialProps (ctx );
175+ const { css, ids } = extractCritical (initialProps .html || ' ' );
176+
177+ return {
178+ ... initialProps ,
179+ styles : (
180+ <>
181+ { initialProps .styles }
182+ <style
183+ data-emotion = { ` css ${ids .join (' ' )} ` }
184+ dangerouslySetInnerHTML = { { __html: css }}
185+ />
186+ </>
187+ ),
188+ };
189+ // ...
190+ }
191+ }
192+ ```
193+
194+ ---
195+
196+ ### React Router v7+
197+
198+ This guide covers [ Framework mode] ( https://reactrouter.com/start/modes#framework ) for React Router.
199+
200+ #### 1. Configure Server Entry
201+
202+ ``` tsx
203+ Update ` entry.server.tsx ` :
204+
205+ import { PassThrough } from ' node:stream' ;
206+ import type { EntryContext } from ' react-router' ;
207+ import { createReadableStreamFromReadable } from ' @react-router/node' ;
208+ import { ServerRouter } from ' react-router' ;
209+ import { renderToPipeableStream } from ' react-dom/server' ;
210+ import { cache , extractCritical , CacheProvider } from ' @leafygreen-ui/emotion' ;
211+
212+ const ABORT_DELAY = 5_000 ;
213+
214+ export default function handleRequest(
215+ request : Request ,
216+ responseStatusCode : number ,
217+ responseHeaders : Headers ,
218+ routerContext : EntryContext ,
219+ ) {
220+ return new Promise <Response >((resolve , reject ) => {
221+ let statusCode = responseStatusCode ;
222+ const chunks: Buffer [] = [];
223+
224+ const { pipe, abort } = renderToPipeableStream (
225+ <CacheProvider value = { cache } >
226+ <ServerRouter context = { routerContext } url = { request .url } />
227+ </CacheProvider >,
228+ );
229+
230+ const collectStream = new PassThrough ();
231+ collectStream .on (' data' , chunk => chunks .push (chunk ));
232+
233+ collectStream .on (' end' , () => {
234+ const html = Buffer .concat (chunks ).toString (' utf-8' );
235+ const { css, ids } = extractCritical (html );
236+ const emotionStyleTag = ` <style data-emotion="css ${ids .join (' ' )}">${css }</style> ` ;
237+ const htmlWithStyles = html .replace (' </head>' , ` ${emotionStyleTag }</head> ` );
238+
239+ const body = new PassThrough ();
240+ const stream = createReadableStreamFromReadable (body );
241+
242+ responseHeaders .set (' Content-Type' , ' text/html' );
243+ resolve (
244+ new Response (stream , {
245+ headers: responseHeaders ,
246+ status: statusCode ,
247+ }),
248+ );
249+
250+ body .write (htmlWithStyles );
251+ body .end ();
252+ });
253+
254+ collectStream .on (' error' , reject );
255+ pipe (collectStream );
256+ setTimeout (abort , ABORT_DELAY );
257+
258+ });
259+ }
260+ ```
261+
262+ #### 2. Configure Client Entry
263+
264+ Update ` entry.client.tsx ` :
265+
266+ ``` tsx
267+ import { startTransition , StrictMode } from ' react' ;
268+ import { hydrateRoot } from ' react-dom/client' ;
269+ import { HydratedRouter } from ' react-router/dom' ;
270+ import { CacheProvider , cache } from ' @leafygreen-ui/emotion' ;
271+
272+ startTransition (() => {
273+ hydrateRoot (
274+ document ,
275+ <StrictMode >
276+ <CacheProvider value = { cache } >
277+ <HydratedRouter />
278+ </CacheProvider >
279+ </StrictMode >,
280+ );
281+ });
282+ ```
283+
284+ ---
285+
286+ ### Gatsby.js
287+
288+ > ⚠️ ** Not Currently Supported**
289+ >
290+ > There is a peer dependency mismatch between ` @leafygreen-ui/emotion ` and ` gatsby-plugin-emotion ` . As a result, we do not currently support GatsbyJS projects out of the box. If you need Emotion in a Gatsby project, refer to the [ Gatsby Emotion documentation] ( https://www.gatsbyjs.com/docs/how-to/styling/emotion/ ) .
0 commit comments