Handling CSS-in-JS Libraries

Handling CSS-in-JS Libraries

Many React applications use CSS-in-JS libraries like styled-components or emotion, which dynamically inject styles. These libraries require specific CSP configurations:

// Styled-components with CSP
import { createGlobalStyle } from 'styled-components';

// Configure styled-components to use nonce
const GlobalStyle = createGlobalStyle`
  body {
    margin: 0;
    padding: 0;
  }
`;

// App.js - Passing nonce to styled-components
function App() {
  // Get nonce from server-injected global variable
  const nonce = window.__CSP_NONCE__;
  
  return (
    <StyleSheetManager nonce={nonce}>
      <GlobalStyle />
      <YourAppComponents />
    </StyleSheetManager>
  );
}

// Server-side rendering with styled-components
import { ServerStyleSheet } from 'styled-components';

app.get('*', (req, res) => {
  const sheet = new ServerStyleSheet();
  const nonce = res.locals.nonce;
  
  try {
    const html = ReactDOMServer.renderToString(
      sheet.collectStyles(
        <StyleSheetManager nonce={nonce}>
          <App />
        </StyleSheetManager>
      )
    );
    
    const styleTags = sheet.getStyleTags(); // Get style tags with nonce
    const finalHtml = indexHtml
      .replace('</head>', `${styleTags}</head>`)
      .replace('<div id="root"></div>', `<div id="root">${html}</div>`);
    
    res.send(finalHtml);
  } finally {
    sheet.seal();
  }
});

For Emotion library:

// emotion-cache.js
import createCache from '@emotion/cache';

export const createEmotionCache = (nonce) => {
  return createCache({
    key: 'css',
    nonce: nonce,
  });
};

// _app.js (Next.js example)
import { CacheProvider } from '@emotion/react';
import { createEmotionCache } from '../utils/emotion-cache';

function MyApp({ Component, pageProps, emotionCache = createEmotionCache() }) {
  return (
    <CacheProvider value={emotionCache}>
      <Component {...pageProps} />
    </CacheProvider>
  );
}

// Server-side configuration
MyApp.getInitialProps = async (appContext) => {
  const nonce = appContext.ctx.res.locals.nonce;
  const emotionCache = createEmotionCache(nonce);
  
  const appProps = await App.getInitialProps(appContext);
  return { ...appProps, emotionCache };
};