How to use 'theme' and 'props' in makeStyles?
Asked Answered
J

7

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

Jugate answered 13/5, 2019 at 11:22 Comment(2)
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: youtu.be/…Crier
If you're using MUI v5, see this answer for an alternative approach.Extremadura
P
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"
Planet answered 21/5, 2019 at 21:20 Comment(3)
@SaadKhan, could you please tell me which version of @material-ui you use? I would like to update Tested with part.Planet
"@material-ui/core": "^4.8.3" Hope this helpsRichie
Totally help me. Thanks! M-UI: 4.12.xTorey
V
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

Vessel answered 13/5, 2019 at 14:53 Comment(0)
D
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.

Duval answered 7/1, 2020 at 17:36 Comment(0)
W
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...
Winona answered 5/9, 2020 at 21:15 Comment(0)
M
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,
    },
}));
Mosby answered 12/1, 2021 at 11:17 Comment(0)
J
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. ✨🔥

Jarred answered 10/9, 2021 at 19:43 Comment(0)
B
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,
  }
}));
Bother answered 14/6, 2023 at 12:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.