withStyles
Last updated
Last updated
It's like the material-ui v4 higher-order component API but type safe by design.
Using as const
can often helps when you get red squiggly lines.
import { withStyles } from "tss-react/mui";
// When using withStyles, if you need to combine classes
// you can use the clsx package. (But use const { cx } = useStyles()
// with makeStyles!)
import clsx from "clsx";
type Props = {
className?: string;
classes?: Partial<Record<"root" | "text", string>>;
colorSmall: string;
};
function MyComponent(props: Props) {
const classes = withStyles.getClasses(props);
return (
// props.classeName and props.classes.root are merged, props.className get higher specificity
<div className={classes.root}>
<span className={classes.text}>The background color should be different when the screen is small.</span>
</div>
);
}
const MyComponentStyled = withStyles(
MyComponent,
(theme, props) => ({
root: {
backgroundColor: theme.palette.primary.main,
height: 100
},
text: {
border: "1px solid red"
},
"@media (max-width: 960px)": {
root: {
backgroundColor: props.colorSmall
}
}
})
);
export default MyComponentStyled;
import MyComponent from "./MyComponent";
render(
<MyComponent
className="foo bar"
classes={{ text: "baz baz" }}
colorSmall="cyan"
/>
);
If you have your styles defined as a separate function:
import { withStyles } from "tss-react/mui";
import type { Theme } from '@mui/material';
import clsx from "clsx";
type Props = {
className?: string;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
colorSmall: string;
};
function MyComponent(props: Props) {
const classes = withStyles.getClasses(props);
return (
// props.classeName and props.classes.root are merged, props.className get higher specificity
<div className={classes.root}>
<span className={classes.text}>The background color should be different when the screen is small.</span>
</div>
);
}
const styles = (theme: Theme, props: Props) => ({
root: {
backgroundColor: theme.palette.primary.main,
height: 100
},
text: {
border: "1px solid red"
},
"@media (max-width: 960px)": {
root: {
backgroundColor: props.colorSmall
}
}
});
const MyComponentStyled = withStyles(MyComponent, styles);
export default MyComponentStyled;
The main reason you would use withStyles
over makeStyles
is to support class based components.
import * as React from "react";
import { withStyles }ย from "tss-react/mui";
// When using withStyles, if you need to combine classes
// you can use the clsx package. (But use const { cx } = useStyles()
// with makeStyles!)
import clsx from "clsx";
export type Props ={
className?: string;
classes?: Partial<Record<"root" | "span", string>>;
isBig: boolean;
};
class MyComponent extends React.Component<Props> {
render() {
const classes = withStyles.getClasses(this.props);
return (
{/* props.classeName and props.classes.root are merged, props.className get higher specificity */}
<div className={classes.root}>
<span className={classes.span}>The background color should be different when the screen is small.</span>
</div>
);
}
}
const MyComponentStyled = withStyles(
MyComponent,
(theme, props) => ({
root: {
backgroundColor: theme.palette.primary.main,
height: props.isBig ? 100 : 50
},
span: {
border: "1px solid red"
},
"@media (max-width: 960px)": {
root: {
backgroundColor: "red"
}
}
})
);
export default MyComponentStyled;
import MyComponent from "./MyComponent";
render(
<MyComponent
className="foo bar"
classes={{ text: "baz baz" }}
colorSmall="cyan"
/>
);
Or, if you have your styles defined as a separate function:
import * as React from "react";
import { withStyles } from "tss-react/mui";
import type { Theme } from '@mui/material';
import clsx from "clsx";
type Props = {
className?: string;
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
colorSmall: string;
};
class MyComponent extends React.Component<Props> {
render() {
const classes = withStyles.getClasses(this.props);
return (
{/* props.classeName and props.classes.root are merged, props.className get higher specificity */}
<div className={classes.root}>
<span className={classes.span}>The background color should be different when the screen is small.</span>
</div>
);
}
}
const styles = (theme: Theme, props: Props) => ({
root: {
backgroundColor: theme.palette.primary.main,
height: 100
},
text: {
border: "1px solid red"
},
"@media (max-width: 960px)": {
root: {
backgroundColor: props.colorSmall
}
}
});
const MyComponentStyled = withStyles(MyComponent, styles);
export default MyComponentStyled;
Your component can also only have a className
prop (and no classes
).
import * as React from "react";
import { withStyles }ย from "tss-react/mui";
export type Props ={
className?: string;
isBig: boolean;
};
class MyComponent extends React.Component<Props> {
render() {
const classes = withStyles.getClasses(this.props);
return (
<div className={classes.root}>
The background color should be different when the screen is small.
</div>
);
}
}
const MyComponentStyled = withStyles(
MyComponent,
(theme, props) => ({
"root": {
"backgroundColor": theme.palette.primary.main,
"height": props.isBig ? 100 : 50
},
"@media (max-width: 960px)": {
"root": {
"backgroundColor": "red"
}
}
})
);
export default MyComponentStyled;
import MyComponent from "./MyComponent";
render(
<MyComponent
className="foo bar"
colorSmall="cyan"
/>
);
You can also pass a mui component like for example <Button />
and you'll be able to overwrite every rule name of the component (it uses the classes
prop).
import Button from "@mui/material/Button";
import { withStyles }ย from "tss-react/mui";
const MyStyledButton = withStyles(Button, {
root: {
backgroundColor: "grey"
}
text: {
color: "red"
},
"@media (max-width: 960px)": {
text: {
color: "blue"
}
}
});
What's very powerfull about the withStyles API it it's capable to infer the type of the nested overwritable classes, example:
import Breadcrumbs from "@mui/material/Breadcrumbs";
import { withStyles } from "tss-react/mui";
const MyBreadcrumbs = withStyles(
Breadcrumbs,
(theme, props, classes) => {
ol: {
[`& .${classes.separator}`]: {
color: theme.palette.primary.main
}
}
}
);
import { withStyles } from "tss-react/mui";
const MyAnchorStyled = withStyles("a", (theme, { href }) => ({
root: {
border: "1px solid black",
backgroundColor: href?.startsWith("https")
? theme.palette.primary.main
: "red"
}
}));
You can experiment with those examples here live here, you can also run it locally with yarn start_spa
.
To ease debugging you can specify a name that will appear in every class names. It is like the option.name
in material-ui v4's makeStyles
.
It's also required to for theme style overrides.
import { withStyles }ย from "tss-react/mui";
const MyDiv = withStyles("div", {
root: {
/* ... */
}
}, { name: "MyDiv" });
//The class apllied to the div will be like: "css-xxxxxx-MyDiv-root"
If you want to use withStyles
instead of styled
for the extra type safety it provides:
Before:
import { styled } from '@mui/material/styles';
import Popper from '@mui/material/Popper';
const StyledPopper = styled(Popper)({
border: '1px solid red',
'& .Mui-autoComplete-listBox': {
boxSizing: 'border-box',
'& ul': {
padding: 0,
margin: 0
}
},
"@media (max-width: 960px)": {
color: "blue"
}
});
After (just wrap everything into root
):
import { withStyles } from 'tss-react/mui';
import Popper from '@mui/material/Popper';
const StyledPopper = withStyles(Popper, {
root: {
border: '1px solid red',
'& .Mui-autoComplete-listBox': {
boxSizing: 'border-box',
'& ul': {
padding: 0,
margin: 0
}
},
"@media (max-width: 960px)": {
color: "blue"
}
}
});