54

How do I write makeStyles() so that it allows me to use both theme variables and props?

I've tried this:

const useStyles = makeStyles(theme => ({
  appbar: props => ({
    boxShadow: "none",
    background: "transparent",
    marginTop: theme.spacing(2),
    marginBottom: theme.spacing(2),
    color: theme.palette.getContrastText(props)
  }),
}));

And called it in the component with:

const classes = useStyles(backgroundColor);

Where backgroundColor is a prop on the component with type CSSProperties["backgroundColor"]

But I'm getting the error:

TypeError: Cannot read property 'rules' of undefined
    at RuleList.onUpdate (C:\Users\...\node_modules\jss\dist\jss.cjs.js:944:14)
    at RuleList.update (C:\Users\...\node_modules\jss\dist\jss.cjs.js:923:12)
    at StyleSheet.update (C:\Users\...\node_modules\jss\dist\jss.cjs.js:1178:39)
    at attach (C:\Users\...\node_modules\@material-ui\styles\makeStyles\makeStyles.js:141:18)
    at C:\Users\...\node_modules\@material-ui\styles\makeStyles\makeStyles.js:262:7
    at useSynchronousEffect (C:\Users\...\node_modules\@material-ui\styles\makeStyles\makeStyles.js:207:14)
    at C:\Users\...\node_modules\@material-ui\styles\makeStyles\makeStyles.js:254:5
    at Layout (C:\Users\...\.next\server\static\development\pages\index.js:1698:17)
    at processChild (C:\Users\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:2888:14)
    at resolve (C:\Users\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:2812:5)
    at ReactDOMServerRenderer.render (C:\Users\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3202:22)
    at ReactDOMServerRenderer.read (C:\Users\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3161:29)
    at renderToString (C:\Users\...\node_modules\react-dom\cjs\react-dom-server.node.development.js:3646:27)
    at render (C:\Users\...\node_modules\next-server\dist\server\render.js:86:16)
    at renderPage (C:\Users\...\node_modules\next-server\dist\server\render.js:211:20)
    at ctx.renderPage (C:\Users\...\.next\server\static\development\pages\_document.js:2404:22)

  100 |   handleSignUpClick,
  101 |   backgroundColor
  102 | }) => {
> 103 |   const classes = useStyles(backgroundColor);
  104 |   return (
  105 |     <AppBar className={classes.appbar}>
  106 |       <Container maxWidth="lg">

edit: I'm using version 4.0.0-beta.1 of material core and styles

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
Jake
  • 3,865
  • 5
  • 25
  • 58
  • I might be late to the party. But, there is this really cool video which walks you through the basics of passing the theme to makeStyles: https://youtu.be/Q4o0GmfNpJc?list=PLQg6GaokU5CwiVmsZ0d_9Zsg_DnIP_xwr&t=703 – codeviper May 04 '21 at 22:07
  • If you're using MUI v5, see [this](https://stackoverflow.com/a/69677803/9449426) answer for an alternative approach. – NearHuscarl Nov 04 '21 at 02:48

7 Answers7

57

Tested with:

"@material-ui/core": "^4.0.0-beta.1",
"@material-ui/styles": "^4.0.0-rc.0",

JavaScript example:

my-component.js

import React from 'react';
import { makeStyles } from '@material-ui/styles';

import { useStyles } from './my-component.styles.js'; 

const myComponent = (props) => {
    const styleProps = { width: '100px', height: '100px' };
    const classes = useStyles(styleProps);

    return (
        <div className={classes.mySelector}></div> // with 100px and height 100px will be applied
    )
}

my-component.styles.js

export const useStyles = makeStyles(theme => ({
    mySelector: props => ({
        display: 'block',
        width: props.width,
        height: props.height,
    }),
}));

TypeScript example:

my-component.ts

import React, { FC } from 'react'; 
import { makeStyles } from '@material-ui/styles';

import { useStyles } from './my-component.styles.ts'; 
import { MyComponentProps, StylesProps } from './my-component.interfaces.ts'; 

const myComponent: FC<MyComponentProps> = (props) => {
    const styleProps: StylesProps = { width: '100px', height: '100px' };
    const classes = useStyles(styleProps);

    return (
        <div className={classes.mySelector}></div> // with 100px and height 100px will be applied
    )
}

my-component.interfaces.ts

export interface StyleProps {
    width: string;
    height: string;
}

export interface MyComponentProps {
}

my-component.styles.ts

import { Theme } from '@material-ui/core';
import { makeStyles } from '@material-ui/styles';

import { StyleProps } from './my-components.interfaces.ts';

export const useStyles = makeStyles<Theme, StyleProps>((theme: Theme) => ({
    mySelector: props => ({ // props = { width: string; height: string }
        display: 'block',
        width: props.width,
        height: props.height,
    }),
}));

Update

Re-tested with

"@material-ui/core": "^4.12.X"
Roman Mahotskyi
  • 4,576
  • 5
  • 35
  • 68
35

You need to pass an object to useStyles rather than a string.

So instead of:

const classes = useStyles(backgroundColor);

you should have:

const classes = useStyles(props);

or

const classes = useStyles({backgroundColor});

Then you can get at backgroundColor using:

color: theme.palette.getContrastText(props.backgroundColor).

Here's a working example: https://codesandbox.io/s/o7xryjnmly

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
3

You can do this: (i dont know if is the best way but works... also can access to the theme ang globals provider (using themeProvider))

import { makeStyles }from '@material-ui/core/styles'
import styles from './styles';

const useStyles =  makeStyles(theme => (styles(theme))); // here call styles function imported from styles.js
const SideNav = ({drawerState, toggleDrawer}) => {

  const classes = useStyles();

return (
 <Box className={classes.root}>

    <Drawer className="drawer" anchor="left" open={drawerState} onClose={() => toggleDrawer(false)}>
      <NavList></NavList>
    </Drawer>

  </Box>
);


export default SideNav;

and in styles.js

const styles = (theme) => {
return ({
    root: {
        '& .drawer': {
            backgroundColor:'red'
        }
    }
});

}

export default styles;

makeStyles get the theme by params so you can create a styles.js for every component with a arrow function inside and calling from makeStyles that can access to the theme provider.

1

We have 2 modes in general, your prop variable is imported to the component or not.

  1. If your prop variable is imported, it is a global variable, so it is valid in makeStyles():
     import {prop} from ...
    
     const useStyles = makeStyles((theme) => ({
       className:{
          // use prop
       }
     })
    
     define component...
  1. If your prop variable is defined in the component (such as a state), you have 2 choices:
  • You can pass the prop variable to makeStyles():

    const useStyles = makeStyles((theme,prop) => ({
      className:{
         // use prop
      }
    })
    
    define component...
  • You can use arrow function without passing any argument (except theme) to makeStyles():
    const useStyles = makeStyles((theme) => ({
      className:(prop)=>({
         //use prop
      })
    })

    define component...
Amirhnr
  • 11
  • 2
0

Here is an example of how you can use only props or props and theme both with makeStyles() just like styled-components

component.js

import { tableCellStyling } from './component.styled.js';

const RenderRow = (props) => {
    const { noPaddingTopBottom } = tableCellStyling(props);
    return(
        <StyledTableRow>
            {data.map( (row,i) => (
                <StyledTableCell className={noPaddingTopBottom}>
                    {row.data}
                </StyledTableCell>
            )}
        </StyledTableRow>
    )
};

Assuming my props object which is being passed by RenderRow Component to tableCellStyling has { color: 'grey', thinRow: true } in it

component.styled.js

import { makeStyles } from '@material-ui/core/styles';

export const tableCellStyling = makeStyles(theme => ({
    noPaddingTopBottom: {
        borderBottom: ({ color }) => color ? `2px solid ${color}` : '2px solid red',
        paddingBottom: props => props.hasActions && 0,
        paddingTop: props => props.hasActions && 0,
        backgroundColor: theme.palette.common.white,
    },
}));
Aadhar Bhatt
  • 641
  • 6
  • 6
0

The way you can do it is like this:

import { makeStyles, createStyles, Theme } from "@material-ui/core/styles";

...
...

const classes = useStyles();

...
...

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: propName => ({
      border: "none",
      boxShadow: "none",
      cursor: propName ? "pointer" : "auto",
      width: "100%",
      backgroundColor: "#fff",
      padding: "15px 15px"
    }),

    updated: {
      marginTop: 12,
      fontWeight: 400,
      color: "#939393"
    }
  })
);

You can use the prop name inside the element you are styling by making it an arrow function that returns the styling. In addition, you can also style other elements inside the createStyles hook. This took me some time, I hope anyone finds it useful. ✨

klevisx
  • 175
  • 1
  • 2
  • 12
0

This is how you can use: Step 1. Install @mui/style using the below command.

npm i @mui/styles --save --force

enter image description here

Step 2: Import and use:

import { createStyles, makeStyles } from '@mui/styles';

const AtomDropDown = React.forwardRef<HTMLButtonElement, AtomDropdownProps>(
    ({ id }, ref): JSX.Element => {
        const component_id: any = id ? id : 'myId';
        const useStyles = makeStyles({
            root: {
                background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
                border: 0,
            },
        });

        const classes = useStyles();

        return (

            <NativeSelect
                className={classes.root}
                id={component_id}
            >
                <option key='key' value='myvalue'>Name</option>
            </NativeSelect>
        )
    }
);
export default AtomDropDown;

For theme:

import { makeStyles, Theme } from '@material-ui/core';

const useStyles = makeStyles((theme: Theme) => ({
  root: {
    minWidth: 140,
  }
}));
Shubham Verma
  • 8,783
  • 6
  • 58
  • 79