Material-UI - Why should I use makeStyles instead of inline styles? - reactjs

In Material-UI, there is the makeStyles function which can be used to get custom CSS-Styling.
Should I use it if I am not using a theme in that specific CSS?
For example:
import React from "react";
import { TextField, Paper, Button, Box } from "#material-ui/core";
const classes = {
paper: {
backgroundColor: "#eee",
marginLeft: "30%",
marginRight: "30%"
},
textField: {
backgroundColor: "#fff"
},
button: {
backgroundColor: "green",
marginLeft: 20
}
};
const Ohne = () => {
console.log(classes);
return (
<Paper style={classes.paper}>
<Box>
<TextField style={classes.textField} />
<Button style={classes.button}>abc</Button>
</Box>
</Paper>
);
};
export default Ohne;
The object was:
{
"paper": {
"backgroundColor": "#eee",
"marginLeft": "30%",
"marginRight": "30%"
},
"textField": {
"backgroundColor": "#fff"
},
"button": {
"backgroundColor": "green",
"marginLeft": 20
}
}
vs
import React from "react";
import { makeStyles } from "#material-ui/styles";
import { TextField, Paper, Button, Box } from "#material-ui/core";
const useStyles = makeStyles(theme => ({
paper: {
backgroundColor: "#eee",
marginLeft: "30%",
marginRight: "30%"
},
textField: {
backgroundColor: "#fff"
},
button: {
backgroundColor: "green",
marginLeft: 20
}
}));
const Mit = () => {
const classes = useStyles();
console.log(classes);
return (
<Paper className={classes.paper}>
<Box>
<TextField className={classes.textField} />
<Button className={classes.button}>abc</Button>
</Box>
</Paper>
);
};
export default Mit;
The object was:
{
"paper": "makeStyles-paper-85",
"textField": "makeStyles-textField-86",
"button": "makeStyles-button-87"
}
So there are 3 main points (that I see):
One way creates classes and references them, the other just uses the object as is.
In the first case an object is assigned to the style property which is inline and has a higher priority.
In the first example it is important to keep the const outside of the class, so the object is still created only once and won't trigger a rerender.
But the resulting component looks identical (in my Firefox).
My questions:
Can an example be constructed where these two approaches result in different controls?
Is there any performance aspect to it?
Any other differences?

There are a few scenarios where using CSS classes (e.g. via makeStyles or withStyles) is necessary:
If you want to use media queries in your CSS
If you want to target pseudo-classes (e.g. :hover)
If you are creating a reusable customization of one of the Material-UI components and want to customize some of the classes that are conditionally applied to the element based on props or some other context (e.g. if you want to customize the "error" look of an Input while leaving it up to the places where the custom component is used to specify the error prop either directly or via the FormControl context)
As far as performance concerns, I would expect inline styles to be faster for most use cases. Whether the difference is enough to matter would depend on a lot of specifics regarding your particular application. The team I work with uses makeStyles or withStyles for most of our styling.
Inline styles can result in a lot of duplicated CSS in the DOM if a particular component is rendered many times within the document (e.g. list items, table cells, etc.). One nice thing about always using classes is that you can change the CSS for the class in the browser's developer tools and see that change applied throughout all its usages in the document.

Related

What is the best way to use sx prop in MUI v5?

I have started using MUI v5 with makeStyles in my previous project. After deploying, I faced a huge delay in loading page's CSS. So I started searching that found out makeStyles is deprecated in MUI v5.
MUI suggests to use sx prop instead. That's fine. But the problem here is I don't want to write my JSX and CSS/JSS code together. For example:
This is what MUI says:
// App.js
function App() {
return (
<Grid sx={{ bgcolor: 'yellow', mx: 5, pt: 2, border: 1 }}>
This is a test!
</Grid>
);
}
export default App;
Below is somehow what I expect:
// style.js
export default {
myGrid: {
bgcolor: 'yellow',
mx: 5,
pt: 2,
border: 1,
},
};
// App.js
import style from "./style";
function App() {
return <Grid sx={style.myGrid}>This is a test!</Grid>;
}
export default App;
I wanna know what is the best pattern to use JSS and JSX files independently with sx? Is it possible to get VSCode suggestions while typing sx props in another file?
You can achieve this with TypeScript. If TypeScript is not already configured in your project, you would have to add it (see this SO answer for help with that).
Then you can make a styles.ts file with all of your component styles by using the SxProps type.
// Styles.ts
import { SxProps } from '#mui/material'
const myGrid: SxProps = {
bgColor: 'yellow',
mx: 5,
pt: 2,
border: 1
}
export default { myGrid }
By using the SxProps type on your stlye object, you'll get code completion with all of the MUI sx props.
Check this out :) We can store styles in a single object.
Use Record<Keys, Type> to define parent object type.
After that you'll get suggestions when you typing keywords. etc: backgroundColor
https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type
//styles.ts
import { SxProps, Theme } from "#mui/material";
export const navBarStyles: Record<string, SxProps<Theme> | undefined> = {
notification: {
backgroundColor: 'white',
"&:hover, &.Mui-focusVisible": { backgroundColor: "white" }
},
test: {
color: 'green'
}
}
Then import navBarStyles
//navbar.tsx
import { navBarStyles } from '../styles/mui/navbar';
Apply Styles
//navbar.tsx
const Navbar = () => {
return (
<IconButton sx={navBarStyles.notification}>
<KeyboardArrowDownRoundedIcon htmlColor='#ffffff' />
</IconButton>
)
}

MaterialUI v5 -> How to style Autocomplete options

I'm trying to apply some basic styles to the options inside the Autocomplete component from MUI v5. I'm just trying to change the background color on hover, and based on whether or not it is selected. I've tried 2 approaches based on the documentation, using a theme, and applying the sx prop to Autocomplete.
Using Theme almost has me there, code below:
import { createTheme, ThemeProvider } from '#mui/material/styles';
const theme = createTheme({
components: {
MuiAutocomplete: {
styleOverrides: {
option: {
'&[aria-selected="true"]': {
backgroundColor: '#e3abed',
},
'&:hover': {
backgroundColor: '#9c27b0',
},
backgroundColor: '#fff',
},
},
},
},
})
I have the ThemeProvider wrapped around my entire app
and the component:
<Autocomplete
options={['1', '2', '3']}
renderInput={(params) => <TextField {...params} label="Priority" />}
onChange={(_e, val) => setPriority(val)}
/>
So, this almost works. However the [aria-selected="true"] styling is only being applied when I am also hovering over another option in the dropdown. Otherwise it's the default grey that comes with the component and I don't understand why.
The second path I have tried is to use the sx prop on the Autocomplete component. In the docs it says you can target child components by their class name: https://mui.com/material-ui/customization/how-to-customize/#overriding-styles-with-class-names
Here is my component:
<Autocomplete
options={['1', '2', '3']}
renderInput={(params) => <TextField {...params} label="Priority" />}
onChange={(_e, val) => setPriority(val)}
sx={{
'& .MuiAutocomplete-option': {
backgroundColor: '#000',
},
'& .Mui-focused': {
backgroundColor: 'red',
},
}}
open
/>
That doesn't appear to be having any effect. I've been working on this for almost 2 hours and can't seem to get to the finish line here. Any help would be greatly appreciated.
I had the same problem; turns out I just needed to explore the Autocomplete API docs' Props section a bit further. There are several customization possibilities if you don't want to deal with global theme customization:
The PaperComponent prop allows customization of "[t]he component used to render the body of the popup." Note that simply using sx on Autocomplete does not work here because (as #bnays-mhz pointed out) the PopperComponent that the PaperComponent is nested in lives outside the main DOM tree (for z-index/modal UX purposes).
The renderGroup prop allows overriding the rendering of option group headers.
The renderOption prop allows overriding the rendering of individual options.
function CustomPaper({ children }) {
return (
<Paper
sx={{
"& .MuiAutocomplete-listbox": {
"& .MuiAutocomplete-option[aria-selected='true']": {
bgcolor: "purple",
"&.Mui-focused": {
bgcolor: "purple",
}
}
},
"& .MuiAutocomplete-listbox .MuiAutocomplete-option.Mui-focused": {
bgcolor: "red",
}
}}
>
{children}
</Paper>
);
}
Following up on Lars' answer, here's an example using a custom Paper component. Just pass in the custom Paper component name to the PaperComponent prop on Autocomplete <Autocomplete PaperComponent={CustomPaper} {...blahBlahOtherProps} />. This way is nice if you don't want to override the theme for all Autocomplete components.
You can override Autocomplete options css by targeting class
'.MuiAutocomplete-popper'
You will have to apply css globally because this node is created outside the root element in DOM.
I too faced this issue and found a solution.
You Can try this to set the css for options, single option and while hovered (focused) in the Autocomplete using 'sx' prop
Easy Way to customize your AutoComplete component which can be used in Mui V5
<Autocomplete
limitTags={1}
disablePortal
id="simple-search"
value={select.region}
onChange={handleChange("region")}
options={region}
sx={{
width: { sm: "100%", md: 340 },
"& + .MuiAutocomplete-popper .MuiAutocomplete-option":
{
backgroundColor: "#363636",
},
"& + .MuiAutocomplete-popper .MuiAutocomplete-option[aria-selected='true']":
{
backgroundColor: "#4396e6",
},
"& + .MuiAutocomplete-popper .MuiAutocomplete-option[aria-selected ='true'] .Mui-focused":
{
backgroundColor: "#3878b4",
},
}}
disableCloseOnSelect
multiple
renderInput={(params) => (
<TextField {...params} label="Region" color="info" />
)}
/>

How to style Input in material ui

I am new to material ui.
I use fifth version.
<InputBase
ref={params.InputProps.ref}
inputProps={params.inputProps}
autoFocus
sx={{
...InputCSS,
}}
/>
const InputCSS = {
width: '100%',
textIndent: '17px',
py: '12px',
fontSize: '20px',
borderRadius: '3px',
border: '1.5px solid rgba(0, 0, 0, 0.4)',
'MuiInputBase-root': { // does not work
borderColor: 'red !important',
color: 'red !important',
},
'&:focus' : { // does not work
...
}
}
I could have used styled('input') and it works, I can set &:focus and it works but I can't type anything in the input.
I want to get rid of the initial border and set focus property.
How can I change the border for this class?
I know that in v5 we can style our components using variants or sx or styled.
What is the best advice for styling mui components? Because almost all the info in the internet uses outdated useStyle or makeStyles which doesn't work with react 18v.
sometimes I just struggle with components styling in mui
You have several ways to customize a Mui component, but my three favorite approaches are:
Styled utility
Sx prop
Custom global theme
So when should I use each of these approaches?
Styled utility
If you want to make a component reusable across your app, go with styled, if you had ever used styled components then styled will be very familiar to you, here is an example of how to use it:
import * as React from 'react';
import Slider, { SliderProps } from '#mui/material/Slider';
import { alpha, styled } from '#mui/material/styles';
// if you are using typescript, don't forget to pass the component props on styled
const SuccessSlider = styled(Slider)<SliderProps>(({ theme }) => ({
width: 300,
color: theme.palette.success.main,
// to override the styles of inner elements,
// you have to use the & selector followed by the class name.
'&.MuiSlider-thumb': {
'&:hover, &.Mui-focusVisible': {
boxShadow: `0px 0px 0px 8px ${alpha(theme.palette.success.main, 0.16)}`,
},
'&.Mui-active': {
boxShadow: `0px 0px 0px 14px ${alpha(theme.palette.success.main, 0.16)}`,
},
},
}));
export default function StyledCustomization() {
return <SuccessSlider defaultValue={30} />;
}
To make the component even more dynamically, you can define custom props as well, like width, color, border and so on, read the styled docs to know more about it.
Sx prop
If you want to make an one time off style in a single component, the easiest way is to use the sx prop, but be careful with this approach because it can lead to a lot of repetition in your code. Following the code you gave:
<InputBase
ref={params.InputProps.ref}
inputProps={params.inputProps}
autoFocus
sx={{
...InputCSS,
}}
/>
const InputCSS = {
width: '100%',
textIndent: '17px',
py: '12px',
fontSize: '20px',
borderRadius: '3px',
border: '1.5px solid rgba(0, 0, 0, 0.4)',
// you should pass the & selector followed by the class that you want to override
'&.MuiInputBase-root': {
// avoid the use of !important
borderColor: 'red',
color: 'red',
},
'&.MuiInputBase-root:focus': {
...
}
}
check it out on codeSandbox. Just a suggestion, depending on your case the component TextField could fit better as it comes with some good styles and you don't need to style it from scratch.
Side note:
Every mui component has an api doc with a css section where you can find all available classes to override, for instance, see the InputBase api docs, also read the docs about sx prop.
Custom theme
At last but not least, if you want to customize your whole app with custom palette, components, typography, breakpoints and so on, no doubt you should use a custom theme, as mui provides a powerful tool called createTheme to do so, what I like the most in custom theme is the possibility to make custom variants of the component, like so:
import * as React from "react";
import { createTheme, ThemeProvider } from "#mui/material/styles";
import Button from "#mui/material/Button";
import { red } from "#mui/material/colors";
// if you are using typescript, you must declare the module with the
// custom properties in order to get access of this property when using the component
declare module "#mui/material/Button" {
// define custom variants
interface ButtonPropsVariantOverrides {
dashed: true;
redVariant: true;
}
}
const defaultTheme = createTheme();
const theme = createTheme({
components: {
MuiButton: {
variants: [
{
// use the variant name defined earlier
props: { variant: "dashed" },
// set the styles for this variant
style: {
textTransform: "none",
border: `2px dashed ${defaultTheme.palette.primary.main}`,
color: defaultTheme.palette.primary.main
}
},
{
props: { variant: "redVariant" },
style: {
border: `2px solid ${red[300]}`,
color: red[600]
}
}
]
}
}
});
export default function GlobalThemeVariants() {
return (
// use the theme provider to get access of custom theme
// and variants within any child component
<ThemeProvider theme={theme}>
<Button variant="dashed" sx={{ m: 1 }}>
Dashed
</Button>
<Button variant="redVariant" color="secondary">
custom red variant
</Button>
</ThemeProvider>
);
}
See the example, also take a look at the custom theme docs. Hope I clarified the things a bit!

Style Material UI textfield

I'm trying to style a material-ui textfield but I don't get it to work the way I want. I just want a plain simple white input field with standard MUI animations. I want the textfield to always have a white background and text to be black.
You find the code on Code Sandbox: https://codesandbox.io/s/material-demo-2coo8?fontsize=14&hidenavigation=1&theme=dark
Thanks in advance!
You can custom style the TextField font and background color with the following code:
const useStyles = makeStyles((theme) => ({
root: {
"& .MuiInputBase-root": {
color: 'black' //or try theme.palette.primary.main
backgroundColor: 'white' //It should be white by default
}
}
}));
Then simply add the 'root' class to your TextField. As info, the above syntax is called JSS. .MuiInputBase-root is a class applied to the input component, which is a subcomponent of the TextField. This article explores the TextField component with dev tools open, so you can understand how the subcomponents work and get styled by MUI.
One more piece of info about the JSS syntax. Notice the 'space' between the '&' and the '.'. This space is important and acts as part of the selector, informing that .MuiInputBase-root class is on a child component of the parent that receive 'root' class styling.
I would highly suggest you to use functional components since it is the future of React.
Below you can see your example as functional component with regular Material-UI styles.
import React from "react";
import Grid from "#material-ui/core/Grid";
import TextField from "#material-ui/core/TextField";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles(theme => ({
root: { backgroundColor: "white" },
box: { backgroundColor: "white" },
input: {
root: { backgroundColor: "white", color: "black" }
}
}));
export default function FunctionalDemo() {
const classes = useStyles();
return (
<Grid
container
direction="row"
justify="center"
alignItems="center"
className={classes.root}
>
<Grid item xs={12} md={6} className={classes.box}>
<form noValidate>
<TextField
id="email"
label="Type here"
variant="filled"
color="secondary"
className={classes.input.root}
/>
</form>
</Grid>
</Grid>
);
}
Code sandbox: https://codesandbox.io/s/material-ui-plain-text-field-x2s48?fontsize=14&hidenavigation=1&theme=dark

Material UI v1 with React - styling buttons

I'm trying to learn how to use Material UI with React.
I have incorporated v1 of Material UI in my project.
Nothing about coding comes intuitively to me, so I struggle to fill the gaps between the clues left in the documentation for resources.
I know I haven't got the hang of this yet, but piecing together what others have been able to do, I have set up a button in my project, as follows:
import React from 'react';
import Button from 'material-ui/Button';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import { fade } from 'material-ui/styles/colorManipulator';
const MyButton = (props) => {
return <span>
<Button {...props} />
</span>
};
export default MyButton;
I then try to use my button in a couple of places:
import React from 'react';
import MyButton from '../materialui/Button.js';
const style = {
background: '#FF5349',
color: 'white',
padding: '0 30px',
// marginBottom: '30px',
};
const Start = () => (
<span>
<MyButton style={style} size="large">
GET STARTED
</MyButton>
</span>
);
export default Start;
I am trying to change the size of the button.
The Material UI documentation indicates that I should be able to toggle the size property by inserting size="large".
The example given in the documentation is:
<Button size="large" className={classes.button}>
Large
</Button>
I've tried to insert size="large" in the const style in my Start component, in the use of MyButton in the start component and in the Button component itself. None of these attempts do anything to alter the size. The text label in the button looks miniature at the moment and I can't figure out how to manipulate the size.
Can anyone see how to increase the size of the button?
Here is how I have been using it.
You need to set the root Class of the button object (or another available class, refer to the documentation for each available class by components)
import React, { Component } from "react";
import { withStyles } from "material-ui/styles";
import Button from "material-ui/Button";
const styles = theme => ({
button: {
width: "300px",
margin: "0 auto",
textTransform: "uppercase",
padding: "20px 30px",
alignSelf: "center",
},
});
class MyCustomButton extends Component {
render() {
const { classes } = this.props;
return (
<Button color="primary" raised={true} classes={{ root: classes.button }} />
);
}
}
export default withStyles(styles)(MyCustomButton);

Resources