Build Chakra UI Container components using react, typescript, styled-components and styled-system
Tags: react, typescript
Published: 2021-09-02 07:48:46

### Introduction

Let us continue building our chakra components using `styled-components` & `styled-system`. Chakra UI has some pretty neat container components namely `Square, Center, Circle and Container`.

- I would like you to first check the [chakra docs] for each of the components [here](https://chakra-ui.com/docs/layout).

- We will **compose** (extend) our `Flex` component to create these components.

- All the code for this tutorial can be found [here](https://github.com/yaldram/chakra-ui-clone) under the **atom-layout-containers** branch.

### Prerequisite

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

- Create a Square component.
- Create a Circle component.
- Create a Center component.
- Create a Container component.
- Create their respective stories.

### Setup

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

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

- Under the `components/atoms/layout` folder create a new folder called containers. Under containers folder create 2 files `index.tsx` and `containers.stories.tsx`.

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

### Square Component

This component will center its child given size (width and height). We can also pass centerContent = false prop if we don't want to center content.

```tsx
import * as React from "react";

import { Flex, FlexProps } from "../flex";

type SqureOmitted = "width" | "height" | "w" | "h";

export interface SquareProps extends Omit<FlexProps, SqureOmitted> {
  centerContent?: boolean;
}

export const Square = React.forwardRef<HTMLDivElement, SquareProps>(
  (props, ref) => {
    const { size, centerContent = true, children, ...delegated } = props;

    const squareProps = centerContent && {
      align: "center",
      justify: "center",
    };

    return (
      <Flex
        ref={ref}
        size={size}
        {...squareProps}
        grow={0}
        shrink={0}
        {...delegated}
      >
        {children}
      </Flex>
    );
  }
);
```

For simplicity we omitted height and width props instead simply pass the size prop.

### Circle Component

We extend the Square Component with a round border radius so that it appears like a Circle, this will come handy if you want to display some icons under a circle.

```tsx
export const Circle = React.forwardRef<HTMLDivElement, SquareProps>(
  (props, ref) => {
    const { size, children, ...delegated } = props;

    return (
      <Square ref={ref} size={size} {...delegated} borderRadius="9999px">
        {children}
      </Square>
    );
  }
);
```

### Center Component

This component will center its child given width and height.

```tsx
interface CenterProps
  extends Omit<FlexProps, "display" | "align" | "justify"> {}

export const Center = React.forwardRef<HTMLDivElement, CenterProps>(
  (props, ref) => {
    const { children, ...delegated } = props;

    return (
      <Flex ref={ref} align="center" justify="center" {...delegated}>
        {children}
      </Flex>
    );
  }
);
```

Because as the name suggest this is a `Center` component, we omit the alignItems and justifyContent props and by default pass these with center values.

### Container Component

This component is used to constrain a content's width to the current breakpoint, while keeping it fluid. It sets `margin-left` and `margin-right` to `auto`, to keep its content centered.

It also sets a default max-width of `60ch` (60 characters), but you can customize this by passing custom maxWidth value. We can also pass centerContent = true prop if we want to center content `inside the container`.

```tsx
export interface ContainerProps extends FlexProps {
  centerContent?: boolean;
}

export const Container = React.forwardRef<HTMLDivElement, ContainerProps>(
  (props, ref) => {
    const { children, centerContent, ...delegated } = props;

    const centerContentProps = centerContent && {
      align: "center",
    };

    return (
      <Flex
        ref={ref}
        width="100%"
        mx="auto"
        maxW="60ch"
        px="10px"
        direction="column"
        {...centerContentProps}
        {...delegated}
      >
        {children}
      </Flex>
    );
  }
);
```

### Story

- With the above our `Container` components are completed, let us create a story.
- Under the `src/components/atoms/layout/containers/containers.stories.tsx` file we add the below story code.
- We will create 3 stories - center, square, container.

```tsx
import * as React from "react";

import { HStack, VStack } from "../stack";
import { Container, Square, Circle, Center } from ".";

export default {
  title: "Atoms/Layout/Containers",
};

export const center = {
  render: () => (
    <Center bg="tomato" h="100px" color="white">
      This is the Center
    </Center>
  ),
};

export const square = {
  render: () => (
    <HStack spacing="4xl">
      <Square size="40px" bg="tomato" color="white">
        1
      </Square>
      <Square
        as="span"
        size="40px"
        bg="tomato"
        color="white"
        centerContent={false}
      >
        2
      </Square>
      <Circle size="40px" bg="tomato" color="white">
        3
      </Circle>
    </HStack>
  ),
};

export const container = {
  render: () => (
    <VStack spacing="4xl">
      <Container color="white" p="md" bg="red500" maxW="900px">
        Not Centered Content
      </Container>
      <Container p="md" bg="yellow200" maxW="900px" centerContent>
        Centered Content
      </Container>
    </VStack>
  ),
};
```

### Build the Library

- Under the `/layout/index.ts` file and paste the following -

```tsx
export * from "./box";
export * from "./flex";
export * from "./stack";
export * from "./containers";
```

- Now `npm run build`.

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

```tsx
import * as React from "react";
import { HStack, Square, Circle } from "chakra-ui-clone";

export function App() {
  return (
    <HStack spacing="4xl">
      <Square size="40px" bg="tomato" color="white">
        1
      </Square>
      <Square
        as="span"
        size="40px"
        bg="tomato"
        color="white"
        centerContent={false}
      >
        2
      </Square>
      <Circle size="40px" bg="tomato" color="white">
        3
      </Circle>
    </HStack>
  );
}
```

### Summary

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