Earlier this week I posted an article on responsive and custom components with Chakra-ui in next Next.js.
I wanted to write a follow up and focus on a responsive layout, and the Chakra-ui Grid Component makes it a breeze and pleasure to do so.
I've made a small demo, displaying some friends on a small profile-like card, building on top of what I explained in my previous article
//pages/index.js
const Home = () => {
const mainRef = useRef();
const dimensions = useDimensions(mainRef, true);
const [size, setSize] = useState('');
useEffect(() => {
if (dimensions) {
let width = dimensions.borderBox.width;
switch (true) {
case width < 499:
setSize('base');
break;
case width >= 499 && width < 696:
setSize('sm');
break;
case width >= 696 && width < 945:
setSize('md');
break;
case width >= 945:
setSize('lg');
break;
}
}
}, [dimensions]);
return (
<>
<Nav />
<Box
w="100vw"
h="100vh"
display="flex"
justifyContent="center"
ref={mainRef}
alignItems="flex-start"
as="main"
>
<Layout>
<MyGrid />
<Box fontSize={{ base: '20px', sm: '24px' }} mt="30px">
<Text display="flex" justifyContent="center">
Width: {dimensions && dimensions.borderBox.width}px
</Text>
<Text display="flex" justifyContent="center">
Custom breakpoint size: {size}
</Text>
</Box>
</Layout>
</Box>
</>
);
};
export default Home;
The index component returns a React Fragment containing a simple custom Nav component and a Layout container component to house our Grid component.
//components/MyGrid.js
import profiles from '../profiles.json';
const MyGrid = () => {
return (
<>
<Grid
templateColumns={{ base: '1fr', lg: 'repeat(2, 1fr)' }}
h="100%"
gap="10px"
overflow="auto"
css={{
'&::-webkit-scrollbar': {
width: '4px',
},
'&::-webkit-scrollbar-track': {
width: '6px',
},
'&::-webkit-scrollbar-thumb': {
background: 'primary',
borderRadius: '24px',
},
}}
>
{profiles.map((profile) => (
<GridItem
key={profile.id}
h="150px"
bg="primary"
borderRadius="10"
color="white"
display="flex"
pl={{ base: '10px', sm: '25px', lg: '15px' }}
alignItems="center"
>
<Box display="flex" alignItems="center">
<ItemAvatar imageSrc={profile.imageSrc} name={profile.name} />
</Box>
<Details
name={profile.name}
title={profile.title}
link={profile.linkSrc}
/>
</GridItem>
))}
</Grid>
</>
);
};
export default MyGrid;
The Grid component imports a json file from our root and we map over that data and for each object in that array, we return a Grid Item from Chakra, with an Avatar component from Chakra and our own custom Details component inside.
//components/Details.js
import { Box, Text } from '@chakra-ui/react';
import Link from 'next/link';
const Details = ({ name, title, link }) => {
return (
<Box
display="flex"
flexDirection="column"
pl={{ base: '10px', sm: '25px', lg: '15px' }}
fontSize={{ base: '12px', sm: '14px', md: '16px', lg: '14px' }}
>
<Text fontSize="20px"> {name} </Text>
<Text as="i">{title}</Text>
<Text color="#03fcfc" as="i">
<Link href={link}>{link}</Link>
</Text>
</Box>
);
};
export default Details;
Throughout this article, you notice the {{base: 'someValue', sm: 'someValue', ...}}
syntax on some of the components props and you can see me explain it in more depth in my other article, but to summarize, it tells the prop to be a certain value, based on the custom breakpoints we defined in the theme.
//styles/ChakraTheme.js
import { extendTheme } from '@chakra-ui/react';
const theme = extendTheme({
colors: {
primary: '#201D29',
},
breakpoints: {
sm: '499px',
md: '696px',
lg: '945px',
},
});
export default theme;
Finally, to help us visualize our breakpoints, we can utilize a hook from Chakra to get the the width of our Box that wraps our Layout component and store it in state, and it will update in a useEffect every time on the component resize.
import { useRef, useState, useEffect } from 'react';
import { Box, Text } from '@chakra-ui/react';
import MyGrid from '../components/MyGrid';
import Layout from '../components/Layout';
import Nav from '../components/Nav';
import { useDimensions } from '@chakra-ui/react';
const Home = () => {
const mainRef = useRef();
const dimensions = useDimensions(mainRef, true);
const [size, setSize] = useState('');
useEffect(() => {
if (dimensions) {
let width = dimensions.borderBox.width;
switch (true) {
case width < 499:
setSize('base');
break;
case width >= 499 && width < 696:
setSize('sm');
break;
case width >= 696 && width < 945:
setSize('md');
break;
case width >= 945:
setSize('lg');
break;
}
}
}, [dimensions]);
return (
<>
<Nav />
<Box
w="100vw"
h="100vh"
display="flex"
justifyContent="center"
ref={mainRef}
alignItems="flex-start"
as="main"
>
<Layout>
<MyGrid />
<Box fontSize={{ base: '20px', sm: '24px' }} mt="30px">
<Text display="flex" justifyContent="center">
Width: {dimensions && dimensions.borderBox.width}px
</Text>
<Text display="flex" justifyContent="center">
Custom breakpoint size: {size}
</Text>
</Box>
</Layout>
</Box>
</>
);
};
export default Home;
On the initial load, the size state is a empty string const [size, setSize] = useState('')
and will be updates in the useEffect, after the first render to the screen.
Every time the dimensions object from Chakra updates, it triggers the function of the useEffect. That function uses a switch statement to determine what to update the size state to.
Top comments (0)