January 16, 2022 • ☕️ 5 min read
日本語

When using material-ui (now renamed to mui) library for React, you have to choose one css-in-js engine. material-ui recommends [@emotion/react](https:// github.com/emotion-js/emotion) for the first class, and interfaces of both css-in-js are almost the same, but in fact, styled-components is still more popular than emotion.

emotion-vs-styled-components.png

However, the combination of material-ui + styled-components + TypeScript is not easy to set up, and it becomes even more difficult when you want to include your own style definitions in the material-ui theme. Today I want to share with you how to set this up.

The method from material-ui’s documentation

Set up the styled-engine in package.json

package.json
Copy
 {
   "dependencies": {
     "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"   },
   "resolutions": {
     "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"   },
 }

Add configuration to paths in tsconfig.json

You need this setting to enable the correct types of css in import { css } from "@mui/material/styles";.

tsconfig.json
Copy
{
   "compilerOptions": {
     "paths": {
       "@mui/styled-engine": ["./node_modules/@mui/styled-engine-sc"]     }
   },
 }

Next.js webpack settings

next.config.js
Copy
const withTM = require('next-transpile-modules')([
  '@mui/material',
  '@mui/system',
]);

module.exports = withTM({
 webpack: (config) => {
   config.resolve.alias = {
     ...config.resolve.alias,
     '@mui/styled-engine': '@mui/styled-engine-sc',
    };
    return config;
  }
});

Adding custom variable types to theme

Since this is a global type definition, it is better to define it in global.d.ts. You can add the following to global.d.ts in the root folder and TypeScript will merge them into the material-ui default theme type:

global.d.ts
Copy
declare module "@mui/material/styles" {
  interface Theme {
    status: {
      info: string;
      danger: string;
    };
  }
  // allow configuration using `createTheme`
  interface ThemeOptions {
    status?: {
      info?: string;
      danger?: string;
    };
  }
}

Create the theme.ts file and pass it to the ThemeProvider

theme.ts
Copy
import { createTheme } from "@mui/material";

export const theme = createTheme({
  status: {
    info: "#2196f3",
    danger: "#ff0000",
  },
});
/pages/_app.tsx
Copy
import type { AppProps } from "next/app";
import { ThemeProvider } from "styled-components";
import { theme } from "../theme";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

export default MyApp;

Try it out

Intellisense is now working.

mui-default-theme.png

But it’s not perfect

If you try to use the css helper function of styled-components:

mui-default-theme-problem.png

Yes. The type of theme is not recognized in css.

The paths setting in tsconfig.json just changes the css helper function from a reference to @emotion to a reference to styled-components, so the types defined in material-ui are not reflected in the css of styled-components.

Solve this problem

Define the following types in global.d.ts:

global.d.ts
Copy
import { Theme as MuiTheme } from '@mui/material/styles';
declare module "@mui/material/styles" {
  interface Theme {
    status: {
      info: string;
      danger: string;
    };
  }
  // allow configuration using `createTheme`
  interface ThemeOptions {
    status?: {
      info?: string;
      danger?: string;
    };
  }
}

// enable types in `css` helper function in `@mui/material/styles`declare module "styled-components" {  interface DefaultTheme extends MuiTheme {}}

Great! Now you can use the css helper function in @mui/material/styles with type checking.

mui-default-theme-problem-fix.png

A better solution

As material-ui is using the context of styled-components, we’re able to use original styled function in styled-components instead of that in @mui/material/styles. Then, the better solution is as follows:

Set up the styled-engine in package.json

package.json
Copy
 {
   "dependencies": {
     "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"   },
   "resolutions": {
     "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"   },
 }

Set webpack in Next.js

next.config.js
Copy
/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: true,
  experimental: {
    styledComponents: true,
  },
};

Adding custom variables’ types to the theme

Since this is a global type definition, it is better to define it in global.d.ts. You can add the following to global.d.ts in the root folder and TypeScript will merge them into the material-ui default theme type:

global.d.ts
Copy
declare module "@mui/material/styles" {
  interface Theme {
    status: {
      info: string;
      danger: string;
    };
  }
  // allow configuration using `createTheme`
  interface ThemeOptions {
    status?: {
      info?: string;
      danger?: string;
    };
  }
}

// enable types in `css` helper function in `@mui/material/styles`
declare module "styled-components" {
  interface DefaultTheme extends MuiTheme {}
}

Create the theme.ts file and pass it to the ThemeProvider

theme.ts
Copy
import { createTheme } from "@mui/material";

export const theme = createTheme({
  status: {
    info: "#2196f3",
    danger: "#ff0000",
  },
});
/pages/_app.tsx
Copy
import type { AppProps } from "next/app";
import { ThemeProvider } from "styled-components";
import { theme } from "../theme";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

export default MyApp;

Try it again

index.tsx
Copy
import styled, { css } from "styled-components";

styledの中:

simple-way-result-styled.png

cssの中も:

simple-way-result-css.png

Finish

It feels good to be able to use type-safe themes. However, material-ui@v5 doesn’t support styled-components very well, so when you’d better consider carefully when choosing the css-in-jss engine for material-ui, or think again, ‘Do I really need/want to use material-ui?‘. It may have another better choice.

Full project is here:

https://github.com/thundermiracle/nextjs-mui-styled-components-with-typescript


Relative Posts

Use UnoCSS with Next.js

June 7, 2022

Debug Next.js+Typescript in VSCode

December 24, 2020

ThunderMiracle

Blog part of ThunderMiracle.com