Search code examples
javascriptreactjsmaterial-uicreate-react-app

How to style Toolbar with Material UI v5 with 'styled' instead of 'makeStyles' with this class instance?


I'm a newbie to JS, ReactJS, and Material UI. I was following a tutorial on building a simple ReactJS webpage, but looks like the person was using Material UI v4 and now that Material UI v5 is out, a few of the APIs and tools used in the tut are outdated (withStyles, makeStyles). Around 10:46 in the video, he is showing how he is creating classes to stylize each part of the Toolbar.

I'm trying to figure out how I could accomplish the same stylizing for the Toolbar and its Typography components using that same exact structure, via injecting it with className?

My Code:

Navbar.js

import React from 'react'
import logo from '../assets/logo.svg'               // imported assets for the navbar
import logoMobile from '../assets/logoMobile.svg'
import { Toolbar, Typography } from '@mui/material' // these two needed for toolbar
import { styled } from '@mui/material'
import CustomButton from './CustomButton'

const styles = styled({
    bar:{       // class name
      paddingTop: "1.15rem",
      backgroundColor: "#fff",
      ['@media (max-width:780px)']:{flexMedia: "column"}, // media queries - for different devices and web-browser sizes
    },
    // logo: {
    //   width: "15%", 
    //   ['@media (max-width:780px)']: {display: "none"},
    // },
    // logoMobile:{
    //   width: "100%", 
    //   display: "none", 
    //   ['@media (max-width:780px)']: {display: "inline-block"},
    // },
    menuItem: {
      cursor: "pointer", 
      flexGrow: 1,
      "&:hover": {color:  "#4f25c8"},
      ['@media (max-width:780px)']: {paddingBottom: "1rem"},
    }

})

function NavBar() {
  const classes = styles()
  return (
    <Toolbar position="sticky" color="rgba(0, 0, 0, 0.87)" className={classes.bar}>
      {/* <img src={logo} className={classes.logo}/>
      <img src={logoMobile} className={classes.logoMobile}/> */}
      <Typography variant="h5" className={classes.menuItem}>
        About Me
      </Typography>
      <Typography variant="h5" className={classes.menuItem}>
        Projects
      </Typography>
      <Typography variant="h5" className={classes.menuItem}>
        Resume
      </Typography>
      <Typography variant="h5" className={classes.menuItem}>
        Contact Me
      </Typography>
      <CustomButton txt="a test button!"></CustomButton>
    </Toolbar>   
  )
}

export default NavBar

And here's what it ends up looking like

enter image description here

vs what it should look like

enter image description here

I tried utilizing {styled } from '@mui/material' but I can only get the toolbar to show. But it will not apply the styling expected, from the video -- confused as to why? Also this question accomplishes the same task, but it doesn't do it the way that I'm asking about.


Solution

  • Because styled create custom components with additional styles, it attaches the styles without specifying and adding class names manually.

    Here is a quick example for a styled version of your code: (live demo on: stackblitz)

    First use styled to create some custom components, in this case based on the MUI components Toolbar and Typography, with some styles attached to them.

    // 👇 This component based on Toolbar
    const MyToolbar = styled(Toolbar)(({ theme }) => ({
      paddingTop: '1.15rem',
      backgroundColor: '#fff',
      color: 'rgba(0, 0, 0, 0.87)',
      position: 'sticky',
      // 👇 Optional: consider to use theme.breakpoints for this
      ['@media (max-width:780px)']: { flexDirection: 'column' },
    }));
    
    // 👇 This component based on Typography
    const MyItem = styled(Typography)(({ theme }) => ({
      cursor: 'pointer',
      flexGrow: 1,
      '&:hover': { color: '#4f25c8' },
      // 👇 Optional: consider to use theme.breakpoints for this
      ['@media (max-width:780px)']: { paddingBottom: '1rem' },
    }));
    

    While the media queries works in here, perhaps consider to use the breakpoints syntax with theme for a potentially cleaner solution.

    // 👇 Optional: replaces @media line in MyToolbar
    [theme.breakpoints.down('md')]: { flexDirection: 'column' }
    
    // 👇 Optional: replaces @media line in MyItem
    [theme.breakpoints.down('md')]: { paddingBottom: '1rem' }
    

    Otherwise, if it is in some (less common) situations that theme is not needed, then {theme} could be omitted in styled syntax, for example:

    // 👇 Only when theme is not needed
    const MyToolbar = styled(Toolbar)({
      paddingTop: '1.15rem',
      backgroundColor: '#fff',
      color: 'rgba(0, 0, 0, 0.87)',
      position: 'sticky',
      ['@media (max-width:780px)']: { flexDirection: 'column' },
    });
    
    // 👇 Only when theme is not needed
    const MyItem = styled(Typography)({
      cursor: 'pointer',
      flexGrow: 1,
      '&:hover': { color: '#4f25c8' },
      ['@media (max-width:780px)']: { paddingBottom: '1rem' },
    });
    

    After attached with styles, the components MyToolbar and MyItem can then be used in the output, for example:

    <MyToolbar>
      <MyItem variant="h5">About Me</MyItem>
      <MyItem variant="h5">Projects</MyItem>
      <MyItem variant="h5">Resume</MyItem>
      <MyItem variant="h5">Contact Me</MyItem>
      <Button>Custom Btn</Button>
    </MyToolbar>
    

    Note that the inline styles position="sticky" color="rgba(0, 0, 0, 0.87)" does not work here, and is moved to the above styled. If inline styles are needed, consider use the sx prop.

    Hope this will help.