DEV Community

Cover image for Styled Components in Material UI (MUI) with Styled Utility
Rasaf Ibrahim
Rasaf Ibrahim

Posted on • Updated on

Styled Components in Material UI (MUI) with Styled Utility

If you are already familiar with styled components, you might want to use that along with the MUI. To use styled-component with MUI, there is styled() utility. The official documentation of MUI doesn't have enough examples with the styled() utility. This article's purpose is to provide enough examples of styled() utility with both Javascript object syntax & CSS like syntax.

 

📑Table of Contents

 

Basic

 

Import
 

// You must import 'styled' utility form mui
import { styled } from '@mui/material/styles';

// Also, you must import the components which you are gonna use with the styled utility
import Box from '@mui/material/Box';
Enter fullscreen mode Exit fullscreen mode

 

JavaScript Object Syntax

 

Let's create a component with styled() utility by using JavaScript object syntax:

const Basic = styled(Box)({

  backgroundColor: 'aliceblue',
  color: 'darkslategray',
  padding: '2rem',
  textAlign:'center'

})
Enter fullscreen mode Exit fullscreen mode

Note:

  • Here, we are using the Box component but instead of the Box component, we can use any other MUI component. We just need to import that component.
  • Instead of a component we can also use an HTML tag. To use HTML tag, we have to put the HTML tag inside quote, styled('div').
  • Here, the variable name is Basic. Always make sure that the first letter of the variable is capital because it will work like a component and a component name has to start with a capital letter.

 

CSS Like Syntax

 

Instead of JavaScript object syntax, we can also have CSS-like syntax:

const Basic = styled(Box)`
  background-color: aliceblue;
  color: darkslategray;
  padding: 2rem;
  text-align: center;
`;
Enter fullscreen mode Exit fullscreen mode

Note:

  • Don't forget the backticks

  • Semicolon after ending backtick is optional.

 

MUI Theme in the styled() Utility

 

Take a look at the default theme of MUI.

 

JavaScript Object Syntax

 

const UsingTheme = styled(Box)(
  ({ theme }) => ({
    backgroundColor: theme.palette.primary.light,
    color: theme.palette.grey[900],
    padding: theme.spacing(2),
    textAlign: 'center',
    ...theme.typography.h6,
  })
)

Enter fullscreen mode Exit fullscreen mode

The following picture is showing the MUI default theme. In the picture, we can see that the h6 is an object. It has fontFamily, fontWeight, fontSize, lineHeight & letterSpacing properties. We want all of them. So, we are destructuring it (...theme.typography.h6,).

mui theme

 

CSS Like Syntax

 

const UsingTheme = styled(Box)(
  ({ theme }) => `
  background-color: ${theme.palette.primary.light};
  color: ${theme.palette.grey[900]};
  padding: ${theme.spacing(2)};
  text-align: center;


  ${ /* here, we can't destructure like javascript object syntax. So, we need to manually access all the properties of 'h6'  */'' }

  font-size: ${theme.typography.h6.fontSize};
  font-weight: ${theme.typography.h6.fontWeight};
  font-family: ${theme.typography.h6.fontFamily};
  line-height: ${theme.typography.h6.lineHeight};
  letter-spacing: ${theme.typography.h6.letterSpacing};
 `,
)

Enter fullscreen mode Exit fullscreen mode

 

rich-text-editor-for-react npm package

Demo | Documentation

 

Media Queries

 

JavaScript Object Syntax

 

Let's see an example of using media queries with JavaScript Object Syntax:

const UsingMediaQuery = styled(Box)(
    ({ theme }) => ({

      backgroundColor: theme.palette.primary.light,
      color: theme.palette.grey[900],

      "@media (min-width:640px)": {

          backgroundColor: theme.palette.primary.dark,
          color: theme.palette.grey[100],
       },


       /* here, we are accessing breakpoints' value from the theme */

       [`@media screen and (min-width: ${theme.breakpoints.values.md}px)`]: {

         backgroundColor: theme.palette.secondary.light,
         color: theme.palette.grey[900],
      },

    })
  )

Enter fullscreen mode Exit fullscreen mode

 

CSS Like Syntax

 


const UsingMediaQuery = styled(Box)(

    ({ theme }) => `

    background-color: ${theme.palette.primary.light};
    color: ${theme.palette.grey[900]};


    @media (min-width: 640px) {

        background-color: ${theme.palette.primary.dark};
        color: ${theme.palette.grey[100]};
    }


   ${ /* here, we are accessing breakpoints' value from the theme */'' }

    @media (min-width: ${theme.breakpoints.values.md}px) {

        background-color: ${theme.palette.secondary.light};
        color: ${theme.palette.grey[900]};
    }

   `,
  )

Enter fullscreen mode Exit fullscreen mode

 

We can't have same media query more than one time in JavaScript Object Syntax

 

Suppose, there is class a box-section in the html and we are styling it by writing normal CSS:


@media (min-width: 640px) {

  .box-section {

      margin: 1rem;

  }

}


@media (min-width: 640px) {

  .box-section {

      padding: 1rem;

  }

}

Enter fullscreen mode Exit fullscreen mode

In the above example, we can see that the both media query is actually same. So, it was enough to have one media query and all the properties inside it. But we have written the same media query twice. Writing same media query twice like the above example has no problem, the code will work perfectly.

But when we write the same media query multiple times while working with styled component's JavaScript Syntax, only the last media query's code will be executed:

const Box_Section = styled(Box)(
    ({ theme }) => ({

      "@media (min-width:640px)": {

            margin:'1rem'

       },

       "@media (min-width:640px)": {

            padding:'1rem'

       },


    )}

  ) 
Enter fullscreen mode Exit fullscreen mode

In the above example, as the both media query is same, only the last media query's code will be executed. So, the Box_Section component will only have padding, not margin.

 

Reason of this problem:
As we are working with JavaScript object here, we must have unique property name. But in the above example, we are having "@media (min-width:640px)" property name twice. So, JavaScript is considering the last property name's value to be the updated version and only having that.

 

Note: This problem will not occur with CSS Like Syntax in styled components. We can have same media query more than one time just like normal CSS.

 

Child Component and Child Element

 

JSX

 

Suppose, we want the following JSX:

<ParentComponent>

      <div>Hi</div>

     <Box className='childComponent'> Hello </Box>

</ParentComponent>

Enter fullscreen mode Exit fullscreen mode

So, we need to create the ParentComponent component and also need to style the child element div and child component Box.

 

JavaScript Object Syntax

 


const ParentComponent = styled(Box)(

  ({ theme }) => ({

    backgroundColor: theme.palette.primary.light,
    color: theme.palette.grey[900],
    padding: theme.spacing(2),
    textAlign: 'center',


    // Child element
    "> div": {
      backgroundColor: theme.palette.error.dark,
      color: theme.palette.grey[100]
    },


    // Child Component (We need to select the class or id which is used in the child component)
    "> .childComponent": {
      backgroundColor: theme.palette.success.dark,
      color: theme.palette.grey[100]
    },

  })
)

Enter fullscreen mode Exit fullscreen mode

Note:

  • I haven't found a way to select an MUI component without the class or an id that is used in that child component. If you know any way other, please write it down in the comment section.

 

CSS Like Syntax

 

const ParentComponent = styled(Box)(
  ({ theme }) => `
  background-color: ${theme.palette.primary.light};
  color: ${theme.palette.grey[900]};
  padding: ${theme.spacing(2)};
  text-align: center;


  ${ /* Child element  */'' }

  > div  {
    background-color: ${theme.palette.error.dark};
    color: ${theme.palette.grey[100]};
  };


  ${ /* Child Component (We need to select the class or id which is used in the child component)  */'' }

   > .childComponent {
    background-color: ${theme.palette.success.dark};
    color: ${theme.palette.grey[100]};
  };


 `,
)

Enter fullscreen mode Exit fullscreen mode

 

Pseudo Classes

 

JSX

 

Suppose, we want the following JSX:

<PseudoClasses>

      <div>Hi</div>

     <Box className='childComponent'> Hello </Box>

</PseudoClasses>
Enter fullscreen mode Exit fullscreen mode

So, we need to create the PseudoClasses component and also need to style the child element div and child component Box with pseudo-classes.

 

JavaScript Object Syntax

 

const PseudoClasses = styled(Box)(

  ({ theme }) => ({

    backgroundColor: theme.palette.primary.light,
    color: theme.palette.grey[900],
    padding: theme.spacing(2),
    textAlign: 'center',

    ":hover": {
      backgroundColor: theme.palette.primary.dark,
      color: theme.palette.grey[100],
    },

    ":active": {
      backgroundColor: theme.palette.warning.dark,
      color: theme.palette.grey[100],
    },


    // Pseudo select child element
    ":hover > div": {
      backgroundColor: theme.palette.error.dark,
    },


    // Pseudo select child component (We need to select the class or id which is used in the child component) 
    ":hover > .childComponent": {
      backgroundColor: theme.palette.success.dark,
    },

  })
)
Enter fullscreen mode Exit fullscreen mode

 

CSS Like Syntax

 

const PseudoClasses = styled(Box)(
  ({ theme }) => `
  background-color: ${theme.palette.primary.light};
  color: ${theme.palette.grey[900]};
  padding: ${theme.spacing(2)};
  text-align: center;


  :hover {
    background-color: ${theme.palette.primary.dark};
    color: ${theme.palette.grey[100]};
  };


  :active {
    background-color: ${theme.palette.warning.dark};
    color: ${theme.palette.grey[100]};
  };


  ${ /* Pseudo select child element  */'' }

  :hover > div  {
    background-color: ${theme.palette.error.dark};
  };


  ${ /* Pseudo select child component (We need to select the class or id which is used in the child component)   */'' }

  :hover > .childComponent {
    background-color: ${theme.palette.success.dark};
  };


 `,
)
Enter fullscreen mode Exit fullscreen mode

 

Sibling Component

 

JSX

 

Suppose, we want the following JSX:

<>
  <MainComponent> Hello </MainComponent>
  <Box className='siblingComponent'> Hi </Box>
</>
Enter fullscreen mode Exit fullscreen mode

So, we need to create the MainComponent and also need to style the sibling component Box.

 

JavaScript Object Syntax

 

const MainComponent = styled(Box)(

  ({ theme }) => ({

    backgroundColor: theme.palette.primary.light,
    color: theme.palette.grey[900],

    // Adjacent Sibling Component (We need to use class or id of the Sibling component)
    "+ .siblingComponent": {
      backgroundColor: theme.palette.success.dark,
      color: theme.palette.grey[100]
    },

  })
)
Enter fullscreen mode Exit fullscreen mode

Note:

  • Instead of a sibling component, if there was a sibling HTML tag, we could have a style that too ("+ div").

  • The adjacent sibling selector (+) is used to select an element that is directly after another specific element.

 

CSS Like Syntax

 

const MainComponent= styled(Box)(

  ({ theme }) => `
  background-color: ${theme.palette.primary.light};
  color: ${theme.palette.grey[900]};


  ${ /* Adjacent Sibling Component (We need to use class or id of the Sibling component) */'' }

   + .siblingComponent {
    background-color: ${theme.palette.success.dark};
    color: ${theme.palette.grey[100]};
  };

 `,
)
Enter fullscreen mode Exit fullscreen mode

 

Props

 

 

JSX

 

Suppose, we want to have a component (TestingProp) where we can pass two props: dark & border. The value of both props is boolean & the value will of these props controls the style of the component.

   <>

   <TestingProps border={true} dark={true}>Hello
   </TestingProps>

  </>
Enter fullscreen mode Exit fullscreen mode

So, we need to create the TestingProps component and also need work with the prop dark & border.

 

JavaScript Object Syntax (Without MUI Theme)

 

const TestingProps = styled(Box, {

    // Configure which props should be forwarded on DOM
   shouldForwardProp: (prop) => prop !== 'dark' && prop!== 'border'

   })

   (({ dark, border }) => ({

   backgroundColor: dark? "black" : "white",
   color: dark? "white" : "black",
   border: border? "1rem solid pink" : 'none'

   }));

Enter fullscreen mode Exit fullscreen mode

What is this shouldForwaredProp?

We may already know that MUI5 uses emotion as a default style engine. This shouldForwardProp is coming from emotion. shouldForwaredProp is used to pass prop. In an example in the official documentation, shouldForwaredProp is used, you can check the example if you want.

 

CSS Like Syntax (Without MUI Theme)

 

const TestingProps4 = styled(Box, {

  // Configure which props should be forwarded on DOM
  shouldForwardProp: (prop) => prop !== 'dark' && prop!== 'border'

  })


  (({ dark, border }) => `

    background-color: ${dark? "black" : "white"};
    color: ${dark? "white" : "black"};
    border: ${border? "1rem solid pink" : 'none'}

  `);

Enter fullscreen mode Exit fullscreen mode

 

JavaScript Object Syntax (With MUI Theme)

 

const TestingProps = styled(Box, {

   // Configure which props should be forwarded on DOM
  shouldForwardProp: (prop) => prop !== 'dark' && prop!== 'border'

  })

  (({ dark, border, theme }) => ({

  backgroundColor: dark? theme.palette.grey[900] : theme.palette.grey[100],
  color: dark? theme.palette.grey[100] : theme.palette.grey[900],
  border: border? `1rem solid ${theme.palette.primary.main}` : 'none'

  }));

Enter fullscreen mode Exit fullscreen mode

 

CSS Like Syntax (With MUI Theme)

 

const TestingProps = styled(Box, {

   // Configure which props should be forwarded on DOM
   shouldForwardProp: (prop) => prop !== 'dark' && prop!== 'border'

  })


  (({ dark, border, theme }) => `

    background-color: ${dark? theme.palette.grey[900] : theme.palette.grey[100]};
    color: ${dark? theme.palette.grey[100] : theme.palette.grey[900]};
    border: ${border? `1rem solid ${theme.palette.primary.main}` : 'none'};

  `);

Enter fullscreen mode Exit fullscreen mode

 

Default Prop Value

 

We can also pass the default value for the props.

TestingProps.defaultProps = {
    dark: false,
    border: false
  }

Enter fullscreen mode Exit fullscreen mode

 

rich-text-editor-for-react npm package

Demo | Documentation

 

 

sx Prop can be used as an Alternative to the styled Utility

 

Setup

With the right setup, the sx prop can replicate all functionalities provided by the Styled utility:

const NewStyledComponent = (props) => {
   const {sx, children, ...extra_props} = props;

   return (
      <Box 
        sx={{
            backgroundColor: 'black',
            ...sx
        }}
        {...extra_props}
      >
        {children}
      </Box> 
   );
}
Enter fullscreen mode Exit fullscreen mode

 

How the Above Setup Ensures the Functionalities of the styled Utility

 

  • The component accepts and renders children, allowing content to be placed inside, similar to a component created using the styled utility.

  • Just as components created with the styled utility can accept the sx prop for additional styling, "NewStyledComponent" also accepts the sx prop.

  • The ...extra_props in "NewStyledComponent" allows for the forwarding of any additional props to the Box component. For instance, props like "onSubmit" and "onClick" can be seamlessly passed and applied. This behavior aligns with components from the styled utility, which also accept a diverse range of props for various scenarios.

 

Additional Advantages of the sx Prop Approach

 

  • Conditional styling becomes more straightforward because we are essentially creating a standard React component. This allows us to accept and pass props as we typically would, without following the specific prop-accepting conventions of the styled utility.

 

  • Working with media queries becomes more straightforward:
const NewStyledComponent = (props) => {
   const {sx, children, ...extra_props} = props;

   return (
      <Box 
        sx={{
            // media query
            width: { xs: '15rem', sm: '12rem' },
            ...sx
        }}
        {...extra_props}
      >
        {children}
      </Box> 
   );
}
Enter fullscreen mode Exit fullscreen mode

 

  • Accessing the theme does not pose much difficulty:
const NewStyledComponent = (props) => {
   const {sx, children, ...extra_props} = props;

   return (
      <Box 
        sx={(theme) => ({
            // accessing theme
            backgroundColor: theme.palette.primary.main,
            ...sx
        })}
        {...extra_props}
      >
        {children}
      </Box> 
   );
}
Enter fullscreen mode Exit fullscreen mode

 


 

That's it. 😃 Thanks for reading. 🎉

Top comments (5)

Collapse
 
farisabuali profile image
Faris H. Abuali • Edited

To summarize shouldForwardProp:

shouldForwardProp is a function that can be used with MUI's styled function to control which props should be passed down to the underlying DOM element. By default, all props passed to a styled component will be forwarded to the underlying element.

However, in some cases, you may want to exclude certain props from being passed down, such as custom props used only for styling. This is where shouldForwardProp comes in.

shouldForwardProp takes a prop name as a string and returns a boolean. If it returns true, the prop will be passed down to the underlying element. If it returns false, the prop will be excluded.

Here's an example of using shouldForwardProp with MUI's styled function:

import { styled } from '@mui/system';

const Box = styled('div', {
  shouldForwardProp: (prop) => prop !== 'customProp',
})`
  /* styles here */
`;

<Box customProp="someValue">This content will be styled</Box>
<Box otherProp="someOtherValue">This content will not be styled</Box>

Enter fullscreen mode Exit fullscreen mode

In this example, the customProp prop will be excluded from being passed down to the underlying div element because the shouldForwardProp function returns false when the prop name is 'customProp'.

Collapse
 
venugopal46 profile image
Venugopal

What are benefits of using styled to creating a normal component?

const Basic = styled(Box)({
  backgroundColor: 'aliceblue',
})
Enter fullscreen mode Exit fullscreen mode

vs

const Basic = (props) => (
  <Box sx={{  backgroundColor: 'aliceblue' }}>
    {props.children}
  </Box>
)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
rasaf_ibrahim profile image
Rasaf Ibrahim • Edited

If you configure your normal component a little bit more, you can achieve all the functionalities of the styled utility. Check out this section: sx Prop can be used as an Alternative to the styled Utility

Collapse
 
farisabuali profile image
Faris H. Abuali • Edited

Thanks a lot! I am new to MUI styled utility and this is much enlightening. 👌🏼

Collapse
 
rasaf_ibrahim profile image
Rasaf Ibrahim

Thank you for your feedback. I am glad that you found the article enlightening. 😀