From ade102251ab8be312c7feda93d03df3743d68f5c Mon Sep 17 00:00:00 2001 From: Adam Rasheed Date: Fri, 12 Dec 2025 18:08:03 -0800 Subject: [PATCH 1/4] [LG-5798] feat: Emotion SSR Support --- .changeset/mean-sloths-wonder.md | 5 + packages/emotion/README.md | 132 ++++++++++++++ packages/emotion/package.json | 3 +- packages/emotion/src/index.ts | 3 + pnpm-lock.yaml | 297 ++++++------------------------- 5 files changed, 193 insertions(+), 247 deletions(-) create mode 100644 .changeset/mean-sloths-wonder.md diff --git a/.changeset/mean-sloths-wonder.md b/.changeset/mean-sloths-wonder.md new file mode 100644 index 0000000000..80cbb3bfab --- /dev/null +++ b/.changeset/mean-sloths-wonder.md @@ -0,0 +1,5 @@ +--- +'@leafygreen-ui/emotion': minor +--- + +This update exports a CacheProvider component for use in Next.JS applications diff --git a/packages/emotion/README.md b/packages/emotion/README.md index 065fba73a8..1f759c1967 100644 --- a/packages/emotion/README.md +++ b/packages/emotion/README.md @@ -47,3 +47,135 @@ import App from './App'; const html = renderStylesToString(renderToString()); ``` + +## SSR + +Emotion generates styles at runtime and injects them into the DOM. With Next.js App Router and Server Components, styles need to be extracted during server 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) or styles won't apply at all. + +> Note: Emotion does not [currently support React Server Components](https://github.com/emotion-js/emotion/issues/2978), so you will need to use `use client` + +> Note: Leafygreen UI components are not all guaranteed to work out of the box even when the Emotion package has been configured correctly. + +### Next.JS (App Router) + +#### Step 1: Create the Emotion Registry + +Create a new file at `src/app/EmotionRegistry.tsx`: + +```tsx +'use client'; + +import { useServerInsertedHTML } from 'next/navigation'; +import { cache } from '@leafygreen-ui/emotion'; +import { CacheProvider } from '@emotion/react'; + +export default function EmotionRegistry({ + children, +}: { + children: React.ReactNode; +}) { + useServerInsertedHTML(() => { + const names = Object.keys(cache.inserted); + if (names.length === 0) return null; + + let styles = ''; + for (const name of names) { + const style = cache.inserted[name]; + if (typeof style === 'string') { + styles += style; + } + } + + return ( + `; + const htmlWithStyles = html.replace('', `${emotionStyleTag}`); + + const body = new PassThrough(); + const stream = createReadableStreamFromReadable(body); + + responseHeaders.set('Content-Type', 'text/html'); + resolve( + new Response(stream, { + headers: responseHeaders, + status: statusCode, + }), + ); + + body.write(htmlWithStyles); + body.end(); + }); + + collectStream.on('error', reject); + pipe(collectStream); + setTimeout(abort, ABORT_DELAY); + + }); +} +``` + +#### 2. Configure Client Entry + +Update `entry.client.tsx`: + +```tsx +import { startTransition, StrictMode } from 'react'; +import { hydrateRoot } from 'react-dom/client'; +import { HydratedRouter } from 'react-router/dom'; +import { CacheProvider, cache } from '@leafygreen-ui/emotion'; + +startTransition(() => { + hydrateRoot( + document, + + + + + , + ); +}); +``` + +--- + +### Gatsby.js + +> ⚠️ **Not Currently Supported** +> +> 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/).