Responsive layout in Next.js with Chakra-ui
Published: 2022-05-27 06:43:36

Earlier this week I posted an [article](https://dev.to/suiteasdesign/lets-create-some-responsive-and-custom-components-with-chakra-ui-in-next-nextjs-4aa1) 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](https://chakra-ui.com/) Grid [Component](https://chakra-ui.com/docs/components/layout/grid) makes it a breeze and pleasure to do so.

I've made a small [demo](https://cerulean-figolla-6b7b28.netlify.app/), displaying some friends on a small profile-like card, building on top of what I explained in my previous [article](https://dev.to/suiteasdesign/lets-create-some-responsive-and-custom-components-with-chakra-ui-in-next-nextjs-4aa1)

![dev-to-demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mo2lbjkxichmwjq1cynm.png)

```javascript

//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](https://reactjs.org/docs/fragments.html#gatsby-focus-wrapper) containing a simple custom Nav component and a Layout container component to house our Grid component.

```javascript

//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.

``` javascript

//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](https://dev.to/suiteasdesign/lets-create-some-responsive-and-custom-components-with-chakra-ui-in-next-nextjs-4aa1), but to summarize, it tells the prop to be a certain value, based on the custom breakpoints we defined in the theme.

```javascript

//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.

```javascript

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](https://chakra-ui.com/docs/styled-system/utility-hooks/use-dimensions) updates, it triggers the function of the useEffect. That function uses a  switch statement to determine what to update the size state to.

You can see the source code [here](https://github.com/MVAodhan/dev-to-demo)
And follow me [here](https://twitter.com/suiteasdesign)