Build Chakra UI SimpleGrid component using react, typescript, styled-components and styled-system
Tags: react, typescript
Published: 2021-09-03 11:54:46

### Introduction

Let us continue building our chakra components using `styled-components` & `styled-system`. In this tutorial we will be cloning the Chakra UI `SimpleGrid` component.

- I would like you to first check the chakra docs for [simplegrid](https://chakra-ui.com/docs/layout/simple-grid).
- We will **compose** (extend) our `Grid` component to create the `SimpleGrid` component. This component is an easy to get started component that does not have the complexities of CSS Grid.
- All the code for this tutorial can be found [here](https://github.com/yaldram/chakra-ui-clone) under the **atom-layout-grid** branch.

### Prerequisite

Please check the previous post where we have completed the Grid & GridItem Components. Also please check the Chakra SimpleGrid Component code [here](https://github.com/chakra-ui/chakra-ui/blob/main/packages/layout/src/simple-grid.tsx). In this tutorial we will -

- Create a SimpleGrid component.
- Create story for the SimpleGrid component.

### Setup

- First let us create a branch, from the main branch run -

```bash
git checkout -b atom-layout-grid
```

- Under grid folder create `simplegrid.tsx` file.

- So our folder structure stands like - **src/components/atoms/layout/grid**.

### SimpleGrid Component

- Now this is really interesting. Let me list the props that this component takes in : -

  1.  columns: - The number of columns you want.
  2.  spacing: - The gap (both vertical and horizontal) between the grid items.
  3.  spacingX: - The horizontal gap between the grid items.
  4.  spacingY: - The vertical gap between the grid items.
  5.  minChildWidth: - The width at which child elements will break into columns. We pass a number for pixel values or a string for any other valid CSS length.

- Let me paste the code for you, if you read the last `Grid` component post, you will get this code.

```tsx
import * as React from "react";
import { ResponsiveValue } from "styled-system";
import { isNumber, isNull, mapResponsive } from "../../../../utils";

import { Grid, GridProps } from "./grid";

function toPx(value: string | number) {
  return isNumber(value) ? `${value}px` : value;
}

function widthToColumns(width: any) {
  return mapResponsive(width, (value) =>
    isNull(value) ? null : `repeat(auto-fit, minmax(${toPx(value)}, 1fr))`
  );
}

function countToColumns(count: any) {
  return mapResponsive(count, (value) =>
    isNull(value) ? null : `repeat(${value}, minmax(0, 1fr))`
  );
}

export interface SimpleGridOptions {
  minChildWidth?: GridProps["minWidth"];
  columns?: ResponsiveValue<number>;
  spacing?: GridProps["gap"];
  spacingX?: GridProps["gap"];
  spacingY?: GridProps["gap"];
}

export interface SimpleGridProps extends GridProps, SimpleGridOptions {}

export const SimpleGrid = React.forwardRef<HTMLDivElement, SimpleGridProps>(
  (props, ref) => {
    const {
      columns,
      spacingX,
      spacingY,
      spacing,
      minChildWidth,
      ...delegated
    } = props;

    const templateColumns = minChildWidth
      ? widthToColumns(minChildWidth)
      : countToColumns(columns);

    return (
      <Grid
        ref={ref}
        gap={spacing}
        columnGap={spacingX}
        rowGap={spacingY}
        templateColumns={templateColumns}
        {...delegated}
      />
    );
  }
);
```

- I would like to draw your attention to the `templateColumns` variable, if you pass the prop `minChildWidth` we will use `auto-fit` because we don't know the number of columns.

- But if we pass `columns` (number of columns) we use the column value to create our columns. Now you should know some CSS Grid to understand this. It is pretty straightforward.

- Look how easy, clear is this API. Try to push yourself for a clean and easy to use API. It won't happen in the first attempt but take inspiration from open source libraries like Chakra UI, read their code. Because many people contribute to these libraries and they bring a lot of ideas which we can learn from.

### Story

- With the above our `Grid` and `GridItem` components are completed, let us create a story.
- Under the `src/components/atoms/layout/grid/grid.stories.tsx` file we add the below story code.
- We will create another story called simpleGrid.

```tsx
import { spacingOptions } from "../../../../theme/spacing";
import { SimpleGrid, SimpleGridProps } from "./simplegrid";

export const simpleGrid = {
  argTypes: {
    minChildWidth: {
      name: "minChildWidth",
      type: { name: "string", required: false },
      defaultValue: "170px",
      description: `The width at which child elements will
      break into columns. Pass a number for pixel values
      or a string for any other valid CSS length.`,
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "-" },
      },
    },
    spacingY: {
      name: "spacingY",
      type: { name: "string", required: false },
      defaultValue: "md",
      description: "The column gap between the grid items.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "-" },
      },
      control: {
        type: "select",
        ...spacingOptions(),
      },
    },
    spacingX: {
      name: "spacingX",
      type: { name: "string", required: false },
      defaultValue: "lg",
      description: "The row gap between the grid items.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "-" },
      },
      control: {
        type: "select",
        ...spacingOptions(),
      },
    },
  },
  render: (args: SimpleGridProps) => (
    <SimpleGrid {...args}>
      <GridItem bg="tomato" height="80px" />
      <GridItem bg="tomato" height="80px" />
      <GridItem bg="tomato" height="80px" />
      <GridItem bg="tomato" height="80px" />
      <GridItem bg="tomato" height="80px" />
      <GridItem bg="tomato" height="80px" />
    </SimpleGrid>
  ),
};
```

- Now run `npm run storybook` check the stories.

### Build the Library

- Under the `/layout/grid/index.ts` paste the following -

```ts
export * from "./grid";
export * from "./simplegrid";
```

- Now `npm run build`.

- Under the folder `example/src/App.tsx` we can test our `Grid` component. Copy paste the following code and run `npm run start` from the `example` directory.

```tsx
import * as React from "react";
import { SimpleGrid, Box } from "chakra-ui-clone";

export function App() {
  return (
    <SimpleGrid columns={2} spacingX="40px" spacingY="20px">
      <Box bg="tomato" height="80px"></Box>
      <Box bg="tomato" height="80px"></Box>
      <Box bg="tomato" height="80px"></Box>
      <Box bg="tomato" height="80px"></Box>
      <Box bg="tomato" height="80px"></Box>
    </SimpleGrid>
  );
}
```

### Summary

There you go guys in this tutorial we created `SimpleGrid` component just like chakra ui and stories for them. You can find the code for this tutorial under the **atom-layout-grid** branch [here](https://github.com/yaldram/chakra-ui-clone). In the next tutorial we will create Text component. Until next time PEACE.