Capítulo 35 de 456

CSS-in-JS

Core Idea

Use uma lib de CSS-in-JS suportada em Client Components no App Router; libs que dependem de extração de estilo em runtime (styled-jsx, styled-components) precisam de um style registry + useServerInsertedHTML pra injetar as regras antes do conteúdo que as usa.

Key Concepts

  • Style registry: Client Component no topo da árvore que coleta as regras CSS geradas durante um render.
  • useServerInsertedHTML: hook (de next/navigation) que injeta as regras coletadas antes de qualquer conteúdo que possa usá-las.
  • Libs suportadas em Client Components: ant-design, chakra-ui, @fluentui/react-components, kuma-ui, @mui/material, @mui/joy, pandacss, styled-jsx, styled-components, stylex, tamagui, tss-react, vanilla-extract. emotion estava em progresso de suporte.
  • compiler.styledComponents: true: flag no next.config.js que habilita styled-components v6+.

Code Examples

'use client'
import { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'

export default function StyledJsxRegistry({ children }: { children: React.ReactNode }) {
  const [jsxStyleRegistry] = useState(() => createStyleRegistry())
  useServerInsertedHTML(() => {
    const styles = jsxStyleRegistry.styles()
    jsxStyleRegistry.flush()
    return <>{styles}</>
  })
  return <StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
}
  • O que demonstra: registry criado uma única vez (lazy state) e flushado a cada render server.
'use client'
import { useState } from 'react'
import { useServerInsertedHTML } from 'next/navigation'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'

export default function StyledComponentsRegistry({ children }: { children: React.ReactNode }) {
  const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet())
  useServerInsertedHTML(() => {
    const styles = styledComponentsStyleSheet.getStyleElement()
    styledComponentsStyleSheet.instance.clearTag()
    return <>{styles}</>
  })
  if (typeof window !== 'undefined') return <>{children}</>
  return <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>{children}</StyleSheetManager>
}
  • O que demonstra: no browser (typeof window !== 'undefined') pula o StyleSheetManager e deixa styled-components assumir normalmente.
import StyledComponentsRegistry from './lib/registry'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <StyledComponentsRegistry>{children}</StyledComponentsRegistry>
      </body>
    </html>
  )
}
  • O que demonstra: registry envolve o children do root layout, não o <html>/<body>.

Anti-patterns

  • Não usar Client Component pro registry: extração de CSS eficiente exige um Client Component no topo da árvore; evita regerar estilos em renders subsequentes e evita enviar CSS no payload de Server Component.
  • styled-jsx sem v5.1.0+: versão mínima exigida para funcionar em Client Components.

Key Takeaways

  1. Processo de opt-in em três passos: style registry, useServerInsertedHTML, Client Component envolvendo o app no SSR inicial.
  2. Durante streaming, estilos de cada chunk são coletados e anexados aos existentes; após hidratação, a lib assume normalmente e injeta estilos dinâmicos.
  3. styled-components requer compiler.styledComponents: true no next.config.js além do registry.
  4. CSS-in-JS com Server Components/Streaming exige que a lib suporte concurrent rendering (React 18+).

Connects To

  • use client / Server Components: registry precisa ser Client Component.
  • next/navigation useServerInsertedHTML: hook central de todo o padrão.