styled-jsx

repository·main·Indexed 27 days ago

https://github.com/vercel/styled-jsx

A library providing full, scoped, and component-friendly CSS support for JSX, compatible with both server-side and client-side rendering. It enables the <style jsx> syntax via a Babel plugin and supports global styles, dynamic styling through interpolated props, and integration with Next.js. The package includes tools for Server-Side Rendering via StyledRegistry, Content Security Policy (CSP) nonce support, and a webpack loader for consuming standard .css files.

Tokens
5.7K
Snippets
25
Records
31
Agent score
93%

What's inside styled-jsx

  1. Configure Content Security Policy (CSP) with nonces

    main

    To support strict CSP, you must generate a unique nonce per request.

    1. Generate a nonce (e.g., using nanoid).
    2. Pass the nonce to registry.styles({ nonce }).
    3. Set a <meta property="csp-nonce" content={nonce} /> tag in your HTML.
    4. Ensure your Content-Security-Policy header includes the same nonce in the style-src directive.
    import nanoid from 'nanoid'
    
    const nonce = Buffer.from(nanoid()).toString('base64') //ex: N2M0MDhkN2EtMmRkYi00MTExLWFhM2YtNDhkNTc4NGJhMjA3
  2. Configure styled-jsx for testing environments

    main

    To avoid generating snapshot noise from jsx classnames and vendor prefixing during tests, use the styled-jsx/babel-test plugin in your test environment. This plugin strips jsx attributes from <style> tags.

    Note: When using styled-jsx/babel-test, you must mock styled-jsx/css to avoid errors, as the css tagged template literals will not be transpiled.

    {
      "env": {
        "production": {
          "plugins": ["styled-jsx/babel"]
        },
        "development": {
          "plugins": ["styled-jsx/babel"]
        },
        "test": {
          "plugins": ["styled-jsx/babel-test"]
        }
      }
    }
  3. Style third-party or child components using :global()

    main

    If a child component does not expose a className or other customization props, you can style it from the parent using the :global() selector. It is recommended to use the child (direct descendant) selector > to limit the scope of the global style to the intended subtree.

    export default () => (
      <div>
        <ExternalComponent />
    
        <style jsx>{`
          /* "div" will be prefixed, but ".nested-element" won't */
    
          div > :global(.nested-element) {
            color: red;
          }
        `}</style>
      </div>
    )
  4. Create dynamic styles via `className` toggling

    main

    You can achieve dynamic styling by toggling class names on the element based on props.

    const Button = props => (
      <button className={'large' in props && 'large'}>
        {props.children}
        <style jsx>{`
          button {
            padding: 20px;
            background: #eee;
            color: #999;
          }
          .large {
            padding: 50px;
          }
        `}</style>
      </button>
    )
  5. Implement Server-Side Rendering with StyledRegistry

    main

    To ensure concurrent-safe style rendering in SSR, use the StyledRegistry component and the useStyleRegistry hook. This allows you to scope styles for each SSR render.

    Key methods:

    • registry.styles(): Returns an array of React components representing the style tags.
    • registry.flush(): Cleans existing styles in the registry (optional if using a standalone registry per SSR render).

    Note: Next.js 12+ manages this registry automatically.

    import React from 'react'
    import ReactDOM from 'react-dom/server'
    import { StyleRegistry, useStyleRegistry } from 'styled-jsx'
    import App from './app'
    
    function Styles() {
      const registry = useStyleRegistry()
      const styles = registry.styles()
      return <>{styles}</>
    }
    
    export default (req, res) => {
      const app = ReactDOM.renderToString(<App />)
      const html = ReactDOM.renderToStaticMarkup(
        <StyleRegistry>
          <html>
            <head>
              <Styles />
            </head>
            <body>
              <div id="root" dangerouslySetInnerHTML={{ __html: app }} />
            </body>
          </html>
        </StyleRegistry>
      )
      res.end('<!doctype html>' + html)
    }
  6. Create dynamic styles via interpolated props

    main

    Any value from the component's render scope (like props or state) is treated as dynamic. For better performance, split static and dynamic styles into two separate <style jsx> tags so only the dynamic parts re-render.

    const Button = props => (
      <button>
        {props.children}
        <style jsx>{`
          button {
            color: #999;
            display: inline-block;
            font-size: 2em;
          }
        `}</style>
        <style jsx>{`
          button {
            padding: ${'large' in props ? '50' : '20'}px;
            background: ${props.theme.background};
          }
        `}</style>
      </button>
    )
  7. Use resolve as a Babel macro

    main

    If you want to use css.resolve without modifying your .babelrc, you can use babel-plugin-macros.

    1. Install dependencies: npm i --save styled-jsx npm i --save-dev babel-plugin-macros
    2. Add babel-plugin-macros to your Babel configuration.
    3. Import css from styled-jsx/macro instead of styled-jsx/css.

    This approach is also compatible with create-react-app as it includes babel-plugin-macros by default.

    npm i --save styled-jsx
    npm i --save-dev babel-plugin-macros
    {
      "plugins": ["babel-plugin-macros"]
    }
    import css from 'styled-jsx/macro'
    
    const { className, styles } = css.resolve`
      a { color: green }
    `
    
    export default () => (
      <div className={className}>
        <Link className={className}>About</Link>
        {styles}
      </div>
    )
  8. Target the root element of a component

    main

    The outermost element of a component automatically receives a unique jsx-{id} classname. To target this 'host' element specifically, use a class on the root element.

    export default () => (
      <div className="root">
        <style jsx>{`
          .root {
            color: green;
          }
        `}</style>
      </div>
    )
  9. Use constants in styled-jsx

    main

    You can use constants (imported from other files) within your template literals. Note that constants defined outside the component scope are treated as static styles.

    import { colors, spacing } from '../theme'
    import { invertColor } from '../theme/utils'
    
    const Button = ({ children }) => (
      <button>
        {children}
        <style jsx>{`
          button {
            padding: ${spacing.medium};
            background: ${colors.primary};
            color: ${invertColor(colors.primary)};
          }
        `}</style>
      </button>
    )
  10. Use one-off global selectors with `:global()`

    main

    Use the :global() pseudo-selector to escape scoping for specific selectors. This is useful for styling third-party components that use specific class names.

    import Select from 'react-select'
    export default () => (
      <div>
        <Select optionClassName="react-select" />
    
        <style jsx>{`
          /* "div" will be prefixed, but ".react-select" won't */
          div :global(.react-select) {
            color: red;
          }
        `}</style>
      </div>
    )
  11. Install and set up styled-jsx

    main

    To use styled-jsx, install the package via npm and add the styled-jsx/babel plugin to your Babel configuration. This enables the <style jsx> syntax in your JSX components.

    npm install --save styled-jsx
    {
      "plugins": ["styled-jsx/babel"]
    }
  12. Register styled-jsx plugins in Next.js

    main

    To register plugins in a Next.js application, create a custom .babelrc file and pass the plugins array within the styled-jsx object inside the next/babel preset.

    {
      "presets": [
        [
          "next/babel",
          {
            "styled-jsx": {
              "plugins": ["styled-jsx-plugin-postcss"]
            }
          }
        ]
      ]
    }