Skip to content

Style Library Interoperability

While you can use the emotion based styling solution provided by Material-UI to style your application, you can also use the one you already know and love (from plain CSS to styled-components).

This guide aims to document the most popular alternatives, but you should find that the principles applied here can be adapted to other libraries. There are examples for the following styling solutions:

Plain CSS

Nothing fancy, just plain CSS.

Edit Button

PlainCssSlider.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

PlainCssSlider.js

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './PlainCssSlider.css';

export default function PlainCssSlider() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className="slider" />
    </div>
  );
}

CSS injection order ⚠️

Note: Most CSS-in-JS solutions inject their styles at the bottom of the HTML <head>, which gives Material-UI precedence over your custom styles. To remove the need for !important, you need to change the CSS injection order. Here's a demo of how it can be done for the default style engine - emotion:

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function PlainCssPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material-UI's styles. */}
    </CacheProvider>
  );
}

Deeper elements

If you attempt to style the Slider, you will likely need to affect some of the Slider's child elements, for example the thumb. In Material-UI, all child elements have an increased specificity of 2: .parent .child {}. When writing overrides, you need to do the same.

The following examples override the slider's thumb style in addition to the custom styles on the slider itself.

PlainCssSliderDeep1.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .MuiSlider-thumb {
  border-radius: 1px;
}

PlainCssSliderDeep1.js

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './PlainCssSliderDeep1.css';

export default function PlainCssSliderDeep1() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className="slider" />
    </div>
  );
}

The above demo relies on the default className values, but you can provide your own class name with the componentsProps API.

PlainCssSliderDeep2.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .thumb {
  border-radius: 1px;
}

PlainCssSliderDeep2.js

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './PlainCssSliderDeep2.css';

export default function PlainCssSliderDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        className="slider"
        componentsProps={{ thumb: { className: 'thumb' } }}
      />
    </div>
  );
}

Global CSS

Explicitly providing the class names to the component is too much effort? You can target the class names generated by Material-UI.

Edit Button

GlobalCssSlider.css

.MuiSlider-root {
  color: #20b2aa;
}

.MuiSlider-root:hover {
  color: #2e8b57;
}

GlobalCssSlider.js

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './GlobalCssSlider.css';

export default function GlobalCssSlider() {
  return <Slider defaultValue={30} />;
}

CSS injection order ⚠️

Note: Most CSS-in-JS solutions inject their styles at the bottom of the HTML <head>, which gives Material-UI precedence over your custom styles. To remove the need for !important, you need to change the CSS injection order. Here's a demo of how it can be done for the default style engine - emotion:

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function GlobalCssPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material-UI's styles. */}
    </CacheProvider>
  );
}

Deeper elements

If you attempt to style the Slider, you will likely need to affect some of the Slider's child elements, for example the thumb. In Material-UI, all child elements have an increased specificity of 2: .parent .child {}. When writing overrides, you need to do the same.

The following example overrides the slider's thumb style in addition to the custom styles on the slider itself.

GlobalCssSliderDeep.css

.MuiSlider-root {
  color: #20b2aa;
}

.MuiSlider-root:hover {
  color: #2e8b57;
}

.MuiSlider-root .MuiSlider-thumb {
  border-radius: 1px;
}

GlobalCssSliderDeep.js

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import './GlobalCssSliderDeep.css';

export default function GlobalCssSliderDeep() {
  return <Slider defaultValue={30} />;
}

Styled Components

stars npm

Change the default styled engine

By default, Material-UI components come with emotion as their style engine. If, however, you would like to use styled-components, you can configure your app by following this example project. Following this approach reduces the bundle size, and removes the need to configure the CSS injection order.

After the style engine is configured properly, you can use the experimentalStyled() utility from @material-ui/core/styles and have direct access to the theme.

Edit Button

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import { experimentalStyled as styled } from '@material-ui/core/styles';

const CustomizedSlider = styled(Slider)`
  color: #20b2aa;

  :hover {
    color: #2e8b57;
  }
`;

export default function StyledComponents() {
  return <CustomizedSlider defaultValue={30} />;
}

Deeper elements

If you attempt to style the Slider, you will likely need to affect some of the Slider's child elements, for example the thumb. In Material-UI, all child elements have an increased specificity of 2: .parent .child {}. When writing overrides, you need to do the same.

The following examples override the slider's thumb style in addition to the custom styles on the slider itself.

import * as React from 'react';
import Slider from '@material-ui/core/Slider';
import { experimentalStyled as styled } from '@material-ui/core/styles';

const CustomizedSlider = styled(Slider)`
  color: #20b2aa;

  :hover {
    color: #2e8b57;
  }

  & .MuiSlider-thumb {
    border-radius: 1px;
  }
`;

export default function StyledComponentsDeep1() {
  return (
    <div>
      <Slider defaultValue={30} />
      <CustomizedSlider defaultValue={30} />
    </div>
  );
}

The above demo relies on the default className values, but you can provide your own class name with the componentsProps API.

import * as React from 'react';
import { experimentalStyled as styled } from '@material-ui/core/styles';
import Slider from '@material-ui/core/Slider';

const CustomizedSlider = styled((props) => (
  <Slider componentsProps={{ thumb: { className: 'thumb' } }} {...props} />
))`
  color: #20b2aa;

  :hover {
    color: #2e8b57;
  }

  & .thumb {
    border-radius: 1px;
  }
`;

export default function StyledComponentsDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <CustomizedSlider defaultValue={30} />
    </div>
  );
}

Theme

By using the Material-UI theme provider, the theme will be available in the theme context of the styled engine too (emotion or styled-components, depending on your configuration).

⚠️ If you are already using a custom theme with styled-components or emotion, it might not be compatible with Material-UI's theme specification. If it's not compatible, you need to render Material-UI's ThemeProvider first. This will ensure the theme structures are isolated. This is ideal for the progressive adoption of Material-UI's components in the codebase.

You are encouraged to share the same theme object between Material-UI and the rest of your project.

const CustomizedSlider = styled(Slider)(
  ({ theme }) => `
  color: ${theme.palette.primary.main};

  :hover {
    color: ${darken(theme.palette.primary.main, 0.2)};
  }
`,
);
<ThemeProvider theme={customTheme}>
  <CustomizedSlider defaultValue={30} />
</ThemeProvider>

Portals

TODO: fill this section after the portal is implemented with the new styled engine.

CSS Modules

stars

It's hard to know the market share of this styling solution as it's dependent on the bundling solution people are using.

Edit Button

CssModulesSlider.module.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

CssModulesSlider.js

import React from 'react';
import Slider from '@material-ui/core/Slider';
// webpack, parcel or else will inject the CSS into the page
import styles from './CssModulesSlider.module.css';

export default function CssModulesSlider() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className={styles.slider} />
    </div>
  );
}

CSS injection order ⚠️

Note: Most CSS-in-JS solutions inject their styles at the bottom of the HTML <head>, which gives Material-UI precedence over your custom styles. To remove the need for !important, you need to change the CSS injection order. Here's a demo of how it can be done for the default style engine - emotion:

import * as React from 'react';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';

const cache = createCache({
  key: 'css',
  prepend: true,
});

export default function CssModulesPriority() {
  return (
    <CacheProvider value={cache}>
      {/* Your component tree. Now you can override Material-UI's styles. */}
    </CacheProvider>
  );
}

Deeper elements

If you attempt to style the Slider, you will likely need to affect some of the Slider's child elements, for example the thumb. In Material-UI, all child elements have an increased specificity of 2: .parent .child {}. When writing overrides, you need to do the same.

The following examples override the slider's thumb style in addition to the custom styles on the slider itself.

CssModulesSliderDeep1.module.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .MuiSlider-thumb {
  border-radius: 1px;
}

CssModulesSliderDeep1.js

import React from 'react';
// webpack, parcel or else will inject the CSS into the page
import styles from './CssModulesSliderDeep1.module.css';
import Slider from '@material-ui/core/Slider';

export default function CssModulesSliderDeep1() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider defaultValue={30} className={styles.slider} />
    </div>
  );
}

The above demo relies on the default className values, but you can provide your own class name with the componentsProps API.

CssModulesSliderDeep2.module.css

.slider {
  color: #20b2aa;
}

.slider:hover {
  color: #2e8b57;
}

.slider .thumb {
  border-radius: 1px;
}

CssModulesSliderDeep2.js

import React from 'react';
// webpack, parcel or else will inject the CSS into the page
import styles from './CssModulesSliderDeep2.module.css';
import Slider from '@material-ui/core/Slider';

export default function CssModulesSliderDeep2() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        className={styles.slider}
        componentsProps={{ thumb: { className: styles.thumb } }}
      />
    </div>
  );
}

Emotion

stars npm

The css prop

Emotion's css() method works seamlessly with Material-UI.

Edit Button

/** @jsx jsx */
import { jsx, css } from '@emotion/react';
import Slider from '@material-ui/core/Slider';

export default function EmotionCSS() {
  return (
    <div>
      <Slider defaultValue={30} />
      <Slider
        defaultValue={30}
        css={css`
          color: #20b2aa;

          :hover {
            color: #2e8b57;
          }
        `}
      />
    </div>
  );
}

Theme

It works exactly like styled components. You can use the same guide.

The styled() API

It works exactly like styled components. You can use the same guide.