DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on

Build Chakra UI SimpleGrid component using react, typescript, styled-components and styled-system

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.
  • 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 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. 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 -
git checkout -b atom-layout-grid
Enter fullscreen mode Exit fullscreen mode
  • 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.
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}
      />
    );
  }
);
Enter fullscreen mode Exit fullscreen mode
  • 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.
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>
  ),
};
Enter fullscreen mode Exit fullscreen mode
  • Now run npm run storybook check the stories.

Build the Library

  • Under the /layout/grid/index.ts paste the following -
export * from "./grid";
export * from "./simplegrid";
Enter fullscreen mode Exit fullscreen mode
  • 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.

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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. In the next tutorial we will create Text component. Until next time PEACE.

Discussion (0)