React Server Components & MUI

Helder Pinhal
Helder Pinhal
Nov 24 2023
Posted in Engineering & Technology

Exploring the harmony between RSC and MUI

React Server Components & MUI

In our previous article into React Server Components, we uncovered their potential. Today, we're delving into the synergy between React Server Components and another powerhouse in the UI framework realm – Material UI (MUI).

Material UI (MUI)

MUI stands tall among the giants of React ecosystem frameworks, offering a rich selection of pre-built components to accelerate your development journey. Aligned with Google's Material UI design principles by default, MUI also boasts an extensive customization system based on Emotion.

Embarking on your MUI journey is a breeze:

1. Install the necessary dependencies:
yarn add @mui/material @emotion/react @emotion/styled
2. Start using it!
import { Button } from '@mui/material';

export default function Example() {
  return <Button variant="contained">Hello world</Button>;
}

Embracing Challenges We Love

Admittedly, the interplay of frameworks, especially with emerging technologies, can be challenging. For React Server Components users, a common stumbling block was encountering a particular exception:

Server Error
Error: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Call Stack
React
node_modules/@mui/base/utils/ClassNameConfigurator.js (6:50)
...

Prior to MUI's 5.14.0 release, utilizing an MUI component triggered this error due to the absence of the "use client" directive. This directive became obligatory with React Server Components, distinguishing Server Components from Client Components. With the inclusion of the directive in MUI 5.14.0, this hurdle was overcome, enabling seamless integration without additional declarations.

If you're using the default Material UI theme, rejoice – your components will effortlessly fall into place. However, when venturing into custom themes, the narrative takes a nuanced turn.

Crafting Your Custom Material UI Theme

Customizing the default theme is a common task, whether for tweaking fonts, colors, or aligning with your company's branding. MUI facilitates this process through the ThemeProvider, with detailed guidance available in their documentation.

For optimal integration with React Server Components and Next.js, the MUI team recommends creating a ThemeRegistry component. This component efficiently manages the Emotion cache provider and handles CSS injection during navigation changes.

// app/ThemeRegistry.tsx
'use client';
import { useState } from 'react';
import createCache from '@emotion/cache';
import { useServerInsertedHTML } from 'next/navigation';
import { CacheProvider } from '@emotion/react';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import theme from '/path/to/your/theme';

// This implementation is from emotion-js
// https://github.com/emotion-js/emotion/issues/2928#issuecomment-1319747902
export default function ThemeRegistry(props) {
  const { options, children } = props;

  const [{ cache, flush }] = useState(() => {
    const cache = createCache(options);
    cache.compat = true;
    const prevInsert = cache.insert;
    let inserted: string[] = [];
    cache.insert = (...args) => {
      const serialized = args[1];
      if (cache.inserted[serialized.name] === undefined) {
        inserted.push(serialized.name);
      }
      return prevInsert(...args);
    };
    const flush = () => {
      const prevInserted = inserted;
      inserted = [];
      return prevInserted;
    };
    return { cache, flush };
  });

  useServerInsertedHTML(() => {
    const names = flush();
    if (names.length === 0) {
      return null;
    }
    let styles = '';
    for (const name of names) {
      styles += cache.inserted[name];
    }
    return (
      <style
        key={cache.key}
        data-emotion={`${cache.key} ${names.join(' ')}`}
        dangerouslySetInnerHTML={{
          __html: options.prepend ? `@layer emotion {${styles}}` : styles,
        }}
      />
    );
  });

  return (
    <CacheProvider value={cache}>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        {children}
      </ThemeProvider>
    </CacheProvider>
  );
}

// app/layout.js
export default function RootLayout(props) {
  const { children } = props;
  return (
    <html lang="en">
      <body>
        <ThemeRegistry options={{ key: 'mui' }}>{children}</ThemeRegistry>
      </body>
    </html>
  );
}

Refer to MUI's official documentation on the Next.js App Router for comprehensive insights.

Conclusion

The emergence of React Server Components marks a significant leap forward, promising sustained improvements. As the community continues refining out-of-the-box compatibility, MUI faces the challenge of adapting its styling mechanism, particularly with the runtime-centric Emotion.

Keep an eye on open issues in both MUI and Emotion for ongoing updates. The journey towards seamless compatibility persists – stay tuned!

As always, we hope you liked this article, and if you have anything to add, we are available via our Support Channel.

Keep up-to-date with the latest news