2

I have found a number of posts regarding overriding styles but nothing quite like the issue I am having. I am creating styled material-ui components and importing them into different parts of my application. I want to be able to override some of my styling from the various parent components.

I have a button component:

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

const useStyles = makeStyles({
  buttonBlue: {
    background: "#09a1e2",
    border: "1px solid #09a1e2",
    borderRadius: "5px",
    color: "#ffffff",
    cursor: "pointer",
    fontSize: "1.25rem",
    padding: ".75rem 1.25rem .75rem 1.25rem",
    "&:hover": {
      backgroundColor: "#ffffff",
      color: "#09a1e2"
    }
  },
  buttonWhite: {
    background: "#ffffff",
    border: "1px solid #666666",
    borderRadius: "5px",
    cursor: "pointer",
    fontSize: "1rem",
    padding: ".5rem",
    width: "6rem",
    "&:hover": {
      backgroundColor: "#666666",
      color: "#ffffff"
    }
  }
});

const MediumButton = props => {
  const color = props.color;
  const classes = useStyles();
  return (
    <div>
      {color === "blue" ? (
        <button className={classes.buttonBlue}>{props.buttonText}</button>
      ) : (
        <button className={classes.buttonWhite}>{props.buttonText}</button>
      )}
    </div>
  );
};

export default MediumButton;

which I will be importing into another component. I want to override some of these styles on my new component but I can't quite figure out how.

Here is what I am trying:

import React from "react";
import { makeStyles } from "@material-ui/core/styles";
import { withContext } from "../../context/AppContext";
import MediumButton from "../components/MediumButton";

const useStyles = makeStyles({
    root : {
        display: "flex",
    },
    homeButton: {
        background: "white",
        border: "1px solid #09a1e2",
        borderRadius: "5px",
        color: "#09a1e2",
        cursor: "pointer",
        fontSize: "1.25rem",
        padding: ".5rem 3.25rem .5rem 3.25rem",
        textDecoration: 'none',
        "&:hover": {
            backgroundColor: "#ffffff",
            color: "#09a1e2"
        }

    }
});

const PageNotFound = () => {

  const classes = useStyles();
  return (
    <div className={classes.root}>

      <MediumButton className={classes.homeButton} />
    </div>
  );
};

export default withContext(PageNotFound);

I'm not sure what I need to call to override the styles I set on my MediumButton component.

Ido
  • 5,363
  • 2
  • 21
  • 30
tdammon
  • 610
  • 2
  • 13
  • 39

2 Answers2

5

If you allow a className property to be specified, you can apply that CSS class in addition to the CSS classes applied by your component (as opposed to instead of as shown in Ido's Option 1). If the overrides are created using makeStyles, then they will win over the default styling so long as the makeStyles call for the overrides is called after importing the component (see my related answers at the end for more details on this aspect -- with typical usage this will just work the way you would want).

Below is a working example of how this could work. Some additional aspects about my example version of MediumButton to make note of:

  • Uses children rather than a buttonText prop
  • Places common styles (those that should be the same regardless of the color prop) into a separate CSS class (mediumButton`) that is always applied
  • Passes through to the button element any additional props not explicitly used in MediumButton. This makes it easy to pass in event handlers (e.g. onClick) or other properties supported by button (e.g. disabled) without needing to add extra code in MediumButton to handle those props.
  • Uses clsx to conveniently combine multiple class names. clsx is used internally within Material-UI, so this won't add anything to your bundle size.

MediumButton.js

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

const useStyles = makeStyles({
  mediumButton: {
    borderRadius: "5px",
    cursor: "pointer",
    fontSize: "1.25rem",
    padding: ".75rem 1.25rem .75rem 1.25rem",
    margin: 8
  },
  buttonBlue: {
    background: "#09a1e2",
    border: "1px solid #09a1e2",
    color: "#ffffff",
    "&:hover": {
      backgroundColor: "#ffffff",
      color: "#09a1e2"
    }
  },
  buttonWhite: {
    background: "#ffffff",
    border: "1px solid #666666",
    "&:hover": {
      backgroundColor: "#666666",
      color: "#ffffff"
    }
  }
});

const MediumButton = ({ className, children, color, ...other }) => {
  const classes = useStyles();
  return (
    <button
      {...other}
      className={clsx(
        classes.mediumButton,
        {
          [classes.buttonBlue]: color === "blue",
          [classes.buttonWhite]: color !== "blue"
        },
        className
      )}
    >
      {children}
    </button>
  );
};

export default MediumButton;

index.js

import React from "react";
import ReactDOM from "react-dom";
import { makeStyles } from "@material-ui/styles";

import MediumButton from "./MediumButton";

const useStyles = makeStyles({
  homeButton: {
    background: "white",
    color: "#09a1e2",
    border: "1px solid #09a1e2",
    padding: ".5rem 3.25rem .5rem 3.25rem",
    textDecoration: "none",
    "&:hover": {
      backgroundColor: "#ffffff",
      color: "#09a1e2"
    }
  }
});
function App() {
  const classes = useStyles();
  return (
    <div className="App">
      <MediumButton onClick={() => alert("You clicked me!")}>
        White Button
      </MediumButton>
      <MediumButton color="blue">Blue Button</MediumButton>
      <MediumButton className={classes.homeButton}>Home Button</MediumButton>
      <MediumButton className={classes.homeButton} color="blue">
        Blue Home Button
      </MediumButton>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit custom component style overrides

Related answers for understanding which styles will win when layering multiple classes generated via makeStyles:

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

Since you created MediumButton, you get to decide how to allow other components to override the default style.

Option 1:

Replace the entire default style
is to send className prop from the father (just like you did), and in MediumButton, apply this styles if provided:

const MediumButton = props => {
  const { className, color } = props;
  const classes = useStyles();

  return (
    <div>
      {color === "blue" ? (
        <button className={className || classes.buttonBlue}>{props.buttonText}</button>
      ) : (
        <button className={className || classes.buttonWhite}>{props.buttonText}</button>
      )}
    </div>
  );
};

Edit Invisible Backdrop

Option 2:

Replace only desired properties, and keep default styles.
Send additonalStyles prop to MediumButton, and handle them in makeStyles:

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

const useStyles = makeStyles({
  ........your other styles........
  additionalStyles: props => ({
    ...props
  }),
});

const MediumButton = props => {
  const { additionalStyles, color } = props;
  const classes = useStyles(additionalStyles);

  return (
    <div>
      {color === "blue" ? (
        <button className={`${classes.buttonBlue} ${classes.additionalStyles}`}>
          {props.buttonText}
        </button>
      ) : (
        <button className={`${classes.buttonWhite} ${classes.additionalStyles}`}>
          {props.buttonText}
        </button>
      )}
    </div>
  );
};

export default MediumButton;

Edit Invisible Backdrop

Ido
  • 5,363
  • 2
  • 21
  • 30
  • This will work to completely override the styles on `MediumButton` but what if I want to keep all of the styles currently provided in the `MediumButton` component and just override the padding? – tdammon Sep 30 '19 at 20:53