I am using MUI components in ReactJs project, for some reason I need customization in some components to make it responsive according to screen width.
I have added media query and pass it as style attribute in the components but not working, any idea?
I am using code like this:
const drawerWidth = {
width: '50%',
'#media(minWidth: 780px)' : {
width: '80%'
}
}
<Drawer
.....
containerStyle = {drawerStyle}
>
</Drawer>
Code is working for web only, on mobile device no effect. Even CSS code is not applying I've checked in developer console. I am using MUI version 0.18.7.
Any help would be appreciated.
PS: As per requirement I need to make some changes according to screen size using CSS.
By using the breakpoints attribute of the theme, you can utilize the same breakpoints used for the Grid and Hidden components directly in your component.
API
theme.breakpoints.up(key) => media query
Arguments
key (String | Number): A breakpoint key (xs, sm, etc.) or a screen width number in pixels.
Returns
media query: A media query string ready to be used with JSS.
Examples
const styles = theme => ({
root: {
backgroundColor: 'blue',
[theme.breakpoints.up('md')]: {
backgroundColor: 'red',
},
},
});
for more information check this out
You were almost right, but you need to use min-width instead of minWidth:
const styles = {
drawerWidth: {
width: '50%',
'#media (min-width: 780px)': {
width: '80%'
}
}
}
You have a typo in the media query. You should use the following syntax and it will work as expected:
const drawerWidth = {
width: '50%',
'#media (min-width: 780px)' : {
width: '80%'
}
}
instead of
const drawerWidth = {
width: '50%',
'#media(minWidth: 780px)' : {
width: '80%'
}
}
In MUI v5, breakpoints can be declared in sx props by specifying an object where the keys are the breakpoint names and the values are the CSS values.
You can see MUI default breakpoints here. The breakpoint names and values can be overrided using createTheme():
const theme = createTheme({
breakpoints: {
values: {
xxs: 0, // small phone
xs: 300, // phone
sm: 600, // tablets
md: 900, // small laptop
lg: 1200, // desktop
xl: 1536 // large screens
}
}
});
return (
<ThemeProvider theme={theme}>
<Box
sx={{
// specify one value that is applied in all breakpoints
color: 'white',
// specify multiple values applied in specific breakpoints
backgroundColor: {
xxs: "red",
xs: "orange",
sm: "yellow",
md: "green",
lg: "blue",
xl: "purple"
}
}}
>
Box 1
</Box>
</ThemeProvider>
);
In the example above, xs: "orange" means set the Box color to orange if the screen width is inside xs range [300, 600).
You can also set the breakpoints using an array consists of the values from the smallest to largest breakpoint:
return (
<ThemeProvider theme={theme}>
<Box
sx={{
backgroundColor: [
"red",
"orange",
// unset, screen width inside this breakpoint uses the last non-null value
null,
"green",
"blue",
"purple"
]
}}
>
Box 2
</Box>
</ThemeProvider>
);
Similiar answer to #Lipunov's, based on #nbkhope's comment
const styles = {
drawerWidth: {
width: '50%',
[theme.breakpoints.up(780)]: {
width: '80%'
}
}
}
I've solved this problem by doing something like this:
const dropzoneStyles =
window.screen.availWidth < 780 ?
{ 'width': '150px', 'height': '150px', 'border': 'none', 'borderRadius': '50%' }
: { 'width': '200px', 'height': '200px', 'border': 'none', 'borderRadius': '50%' };
and then appending it as an attribute in the Material UI element:
<Dropzone style={dropzoneStyles} onDrop={this.handleDrop.bind(this)}>
So the key is to find out the window screen using window.screen.availWidth. And you would be doing this in the render() function. Hope that helps!
In the style property on React you can only define properties that you can define in a normal DOM element (You can't include media queries for example)
The way you can include media queries for that component would be passing a class name to the Drawer Component
<Drawer containerClassName="someClass" />
And then in a CSS file you do something like this
#media(min-width: 780px){
.someClass {
width: 50%!important;
}
}
In my case I just needed the breakpoint on one component and I found the createTheme approach a little bit too much. I ended using useMediaQuery and useTheme.
I see that with useMEdiaQuery you can be quite granular
import { useTheme } from '#mui/material/styles';
import useMediaQuery from '#mui/material/useMediaQuery';
const Component = () => {
const theme = useTheme();
const matchesSM = useMediaQuery(theme.breakpoints.down('sm'));
const matchesMD = useMediaQuery(theme.breakpoints.only('md'));
const dynamicStyles = {
...matchesSM && {margin: '10px 0'},
...matchesMD && {margin: '20px 0'}
}
return (
<Grid item xs={12} md={4} sx={{...dynamicStyles}}>
<div>Children</div>
</Grid>
)
}
CSS media queries are the idiomatic approach to make your UI responsive. The theme provides five styles helpers to do so:
theme.breakpoints.up(key)
theme.breakpoints.down(key)
theme.breakpoints.only(key)
theme.breakpoints.not(key)
theme.breakpoints.between(start, end)
In the following stress test, you can update the theme color and the background-color property live:
const styles = (theme) => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('md')]: {
backgroundColor: theme.palette.secondary.main,
},
[theme.breakpoints.up('md')]: {
backgroundColor: theme.palette.primary.main,
},
[theme.breakpoints.up('lg')]: {
backgroundColor: green[500],
},
},
});
<Root>
<Typography>down(md): red</Typography>
<Typography>up(md): blue</Typography>
<Typography>up(lg): green</Typography>
</Root>
Know more
Create a variable and then use that variable anywhere in the function
import React from 'react';
import { createMuiTheme, ThemeProvider, useTheme } from '#materialui/core/styles';
import useMediaQuery from '#material-ui/core/useMediaQuery';
function MyComponent() {
const theme = useTheme();
const matches = useMediaQuery(theme.breakpoints.up('sm')); // Variable for media query
return <span hidden={matches}>Hidden on screen size greater then sm </span>;
}
const theme = createMuiTheme();
export default function ThemeHelper() {
return (
<ThemeProvider theme={theme}>
<MyComponent />
</ThemeProvider>
);
}
Related
I'm upgrading a project from material ui v4 to v5 and struggle to update classes/styles properly.
this is a sandBox :
https://codesandbox.io/s/69629346-mui-v5-theming-with-emotion-mui-forked-2j8vze?file=/demo.tsx:1611-1618
In this code 2 box are displayed with 2 ways of applying style. I want to avoid using makeStyles and use SX/emotion as recommanded.
So backgroundColor is red, on hover it become blue.
It works on both.
Now if i click the switch, the backgroundColor become yellow, but on hover of second box the color stay blue instead of grey.
what i'm missing ? thanks
import React, { useState } from "react";
import { makeStyles } from "#mui/styles";
import clsx from "clsx";
import { Box, Switch } from "#mui/material";
import { createTheme, ThemeProvider } from "#mui/material/styles";
const theme = createTheme();
const useStyles = makeStyles((theme) => ({
imageWithBorder: {
height: theme.spacing(10),
width: theme.spacing(30),
padding: theme.spacing(2),
margin: theme.spacing(2),
backgroundColor: "red",
"&:hover": {
backgroundColor: "blue"
}
},
greyHover: {
backgroundColor: "yellow",
"&:hover": { backgroundColor: "grey" }
}
}));
const styles = {
imageWithBorder: {
height: 80,
width: 240,
padding: 2,
margin: 2,
backgroundColor: "red",
"&:hover": {
backgroundColor: "blue"
}
},
greyHover: {
backgroundColor: "yellow",
"&:hover": { backgroundColor: "grey" }
}
};
export default function Test() {
const classes = useStyles();
const [checked, setChecked] = useState(false);
return (
<Box sx={{ display: "flex", flexDirection: "column" }}>
<Box>
Enable grey hover : <Switch checked={checked} onChange={handleChange} />
</Box>
<p>1 With clsx & useStyles</p>
<Box
className={clsx(classes.imageWithBorder, checked && classes.greyHover)}
/>
<p>2 With sx & plain styles</p>
<Box sx={[styles.imageWithBorder, checked && styles.greyHover]} />
</Box>
);
function handleChange(event) {
setChecked(event.target.checked);
}
}
export default function BasicUsage() {
return (
<ThemeProvider theme={theme}>
<Test />
</ThemeProvider>
);
}
By some reason mui doesn't accept backgroundColor: "grey". It's not even render it in the output css.
See gif
Instead, use gray or hex value.
https://codesandbox.io/s/69629346-mui-v5-theming-with-emotion-mui-forked-d0npw6?file=/demo.tsx
I've tried using ImageList component instead of Grid as I just need a grid of photos with titles and it seems to be the whole point of ImageList. My issue is that unlike with Grid I cannot pass breakpoint props for different screen sizes (which I find weird as this would seem logical) so that I can get different count of columns on different screens. What would be the best approach to adjust number of columns based on screen size?
ImageList uses CSS grid and needs the col prop to set the grid-template-columns but without any responsive API baked in. You can swap the ImageList with a Box component with the display set to grid, and uses the sx prop to declare the column template value depend on the screen size, but first let define some breakpoints:
const theme = createTheme({
breakpoints: {
values: {
mobile: 0,
bigMobile: 350,
tablet: 650,
desktop: 900
}
}
});
Then in the component, you can start using it like this:
import ImageListItem, { imageListItemClasses } from "#mui/material/ImageListItem";
<ThemeProvider theme={theme}>
<Box
sx={{
display: "grid",
gridTemplateColumns: {
mobile: "repeat(1, 1fr)",
bigMobile: "repeat(2, 1fr)",
tablet: "repeat(3, 1fr)",
desktop: "repeat(4, 1fr)"
}
// standard variant from here:
// https://github.com/mui-org/material-ui/blob/3e679ac9e368aeb170d564d206d59913ceca7062/packages/mui-material/src/ImageListItem/ImageListItem.js#L42-L43
[`& .${imageListItemClasses.root}`]: {
display: "flex",
flexDirection: "column"
}
}}
>
{itemData.map((item) => <ImageListItem {...}/>)}
</Box>
</ThemeProvider>
Live Demo
References
Media queries in MUI components
https://mui.com/customization/breakpoints/#main-content
I used the useMediaQuery hook to get the cols props for the ImageList component.
import { ImageList, ImageListItem, useMediaQuery } from '#mui/material';
function Gallery() {
const matches = useMediaQuery('(min-width:600px)');
return (
<ImageList cols={matches ? 3 : 2} variant="masonry">
<ImageListItem>
...
</ImageListItem>
</ImageList>
);
}
I spotted a similar problem. The ImageList renders a <ul> tag in DOM. Hence I created my own ImageList styled <ul> component which works fine with ImageListItem. Here as per the gridTemplateColumns attribute for screens with display size sm will show 2 images, display size md will show 4 images
and display size lg will show 5 images.
import * as React from 'react';
import ImageListItem from '#mui/material/ImageListItem';
import { styled } from '#mui/material/styles';
const ImageGalleryList = styled('ul')(({ theme }) => ({
display: 'grid',
padding: 0,
margin: theme.spacing(0, 4),
gap: 8,
[theme.breakpoints.up('sm')]: {
gridTemplateColumns: 'repeat(2, 1fr)'
},
[theme.breakpoints.up('md')]: {
gridTemplateColumns: 'repeat(4, 1fr)'
},
[theme.breakpoints.up('lg')]: {
gridTemplateColumns: 'repeat(5, 1fr)'
},
}));
export default function ImageGallery({imageData}) {
return (
<ImageGalleryList>
{itemData.map((item) => (
<ImageListItem key={item.img}>
// Replace this with your ImageListItem
</ImageListItem>
))}
</ImageGalleryList>
);
}
This solution I came up with works, but seems like a lot of lines for something that Grid handles out of the box. Doesn't ImageList have some built in responsive design implementation?
export function Example(props) {
// not sure if there is a way to get something like this dictionary from React?
const breakpoints = {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920
}
const getColumns = (width) => {
if (width < breakpoints.sm) {
return 2
} else if (width < breakpoints.md) {
return 3
} else if (width < breakpoints.lg) {
return 6
} else if (width < breakpoints.xl) {
return 7
} else {
return 8
}
}
const [columns, setColumns] = useState(getColumns(window.innerWidth))
const updateDimensions = () => {
setColumns(getColumns(window.innerWidth))
}
useEffect(() => {
window.addEventListener("resize", updateDimensions);
return () => window.removeEventListener("resize", updateDimensions);
}, []);
return (
<ImageList cols={columns}>
{/* list items ... */}
</ImageList>
)
}
Instead of using ImageList I used "Image masonry" seems to work.
https://mui.com/material-ui/react-masonry/#main-content
<Masonry columns={{ xs: 1, sm: 2, md: 3, lg: 4, xl: 5 }} spacing={{ xs: 1, sm: 2 }}>
Image masonry
This example demonstrates the use of Masonry for images. Masonry orders its children by row. If you'd like to order images by column, check out ImageList.
I resolved this by overriding column-count property on ImageList (root component)
So, you can add breakpoints using sx props
<ImageList
sx={{
columnCount: {
xs: '1 !important',
sm: '2 !important',
md: '3 !important',
lg: '4 !important',
xl: '5 !important',
},
}}
>
{/* list items ... */}
</ImageList>
I'd like to create a reusable component using Material-UI's api (not using styled-components.) I got this far - and it almost works - but the settings that use theme variable don't work (e.g, bgcolor and padding). Am I doing something wrong - or is this not possible?
const BigPanel = styled(Box)({
display: 'flex',
width: '100%',
flexgrow: 1,
bgcolor: 'background.paper',
borderRadius: 10,
boxShadow:'1',
p:{ xs: 4, md: 8 }
});
The object passed to styled is intended to be CSS properties, but you have a mixture of CSS properties and Box props (bgcolor, p). Even the ones that are valid CSS properties (display, width) are also valid Box props, so the most straightforward solution is to specify all of them as props.
One way to handle this is to use defaultProps. This makes it very easy to override some of the props when using the component by specifying them explicitly as shown in the example below.
import React from "react";
import Box from "#material-ui/core/Box";
import CssBaseline from "#material-ui/core/CssBaseline";
import { styled } from "#material-ui/core/styles";
const BigPanel = styled(Box)({});
BigPanel.defaultProps = {
display: "flex",
width: "100%",
borderRadius: 10,
flexGrow: 1,
bgcolor: "background.paper",
p: { xs: 4, md: 8 },
boxShadow: "1"
};
export default function App() {
return (
<>
<CssBaseline />
<BigPanel>Default BigPanel</BigPanel>
<BigPanel bgcolor="primary.main" color="primary.contrastText">
BigPanel with explicit props
</BigPanel>
</>
);
}
In the example above, styled isn't really serving any purpose anymore except to create a new component type. Though it isn't less code, below is an alternative way to get the same effect without using styled:
const BigPanel = React.forwardRef(function BigPanel(props, ref) {
return <Box ref={ref} {...props} />;
});
BigPanel.defaultProps = {
display: "flex",
width: "100%",
borderRadius: 10,
flexGrow: 1,
bgcolor: "background.paper",
p: { xs: 4, md: 8 },
boxShadow: "1"
};
I want to increase the font size for the pagination part of the material table footer.
In the below image, I am able to change the font size of rows per page with the below code
[1]: https://i.stack.imgur.com/cjNmp.png
components={{
Pagination: props => (
<TablePagination
{...props}
SelectProps={{
style:{
fontSize: 20
}
}}
/>
)
}}
but still unable to change increase size of the whole underlined part
I styled both caption area using two different methods: One via toolbar styles and the other directly:
There is two parts in the results per page component and are a p by default
import makeStyles from "#material-ui/core/styles/makeStyles";
const useStyles = makeStyles({
caption: {
color: "green",
padding: 8,
border: "1px dashed grey",
fontSize: "0.875rem"
},
toolbar: {
"& > p:nth-of-type(2)": {
fontSize: "1.25rem",
color: "red",
fontWeight: 600
}
}
});
// then later
const classes = useStyles();
<TablePagination
// ...
classes={{
toolbar: classes.toolbar,
caption: classes.caption
}}
/>
Here is a codesandbox demo
I am overriding the <ExpansionPanelSummary/> component in Material UI to have less margin. I am using a theme with overrides to do this.
const theme = createMuiTheme({
overrides: {
MuiExpansionPanelSummary: {
expanded: {
marginTop: 5,
marginBottom: 5,
},
content: {
marginTop: 5,
marginBottom: 5,
},
}
},
The problem I run into however is that in the Material-UI built in css, there are two classes being applied at the same time: content and expanded.
.MuiExpansionPanelSummary-content-567.MuiExpansionPanelSummary-expanded-564 {
margin: 20px 0;
}
How can I override multiple applied classes? Is it possible to create a theme rule for this?
Got this working today. The margin you want is on the expanded class on content, so the rules need to look like this to get higher CSS specificity. Look for '&.expanded'.
const useStyles = makeStyles(theme => ({
expansionPanelSummaryContent: {
backgroundColor: 'red',
'&.expanded': {
margin: 0,
backgroundColor: 'yellow',
},
},
}));
export default function MyComponent(props) {
const classes = useStyles();
return (
<ExpansionPanel expanded={props.expanded}>
<ExpansionPanelSummary
classes={{
content: classes.expansionPanelSummaryContent,
expanded: 'expanded'
}}
>
...
</ExpansionPanelSummary>
</ExpansionPanel>
);
}