Introduction
Let us continue building our chakra components clone using styled-components
& styled-system
. In this tutorial we will be cloning the Chakra UI Flex
component.
- I would like you to first check the chakra docs for flex.
- We will compose (extend) our
Box
component to create theFlex
component and further extend theFlex
component to create aSpacer
component. - All the code for this tutorial can be found under the atom-layout-flex branch here.
Prerequisite
Please check the previous post where we have completed the Box Component. Also please check the Chakra Flex Component code here. In this tutorial we will -
- Create a Flex component.
- Create a Spacer component.
- Create story for the Flex component.
Setup
- First let us create a branch, from the main branch run -
git checkout -b atom-layout-flex
Under the
components/atoms/layout
folder create a new folder calledflex
. Under flex folder create 2 filesindex.tsx
andflex.stories.tsx
.So our folder structure stands like - src/components/atoms/layout/flex.
Flex Component
- First lets import the necessary stuff -
import * as React from "react";
import styled from "styled-components";
import { system, FlexboxProps } from "styled-system";
import { Box, BoxProps } from "../box";
- Compose the Box component to create the
BaseFlex
styled component. By composing ourBox
, we extend it meaning ourBaseFlex
also takes in all props that we pass toBox
, inherits the variants and the system extensions we had for ourBox
(marginStart & marginEnd).
const BaseFlex = styled(Box)`
display: flex;
${system({
direction: {
property: "flexDirection",
},
align: {
property: "alignItems",
},
justify: {
property: "justifyContent",
},
wrap: {
property: "flexWrap",
},
})}
`;
We also are extending the system using the system function so that we can have shorthands for our props like
align instead of alignItems
, just like chakra ui flex props.If the above code makes no sense, I request you to please check the previous post. Also check my introductory post.
Let us create the type for our props, now given that we are composing our Box component for making our Flex component it will inherit all props by default, but we don't want the user to pass display prop to our Flex component so we will omit that prop from the type. Let me show you what I mean the below code is pretty self-explanatory -
type FlexOmitted = "display";
type FlexOptions = {
direction?: FlexboxProps["flexDirection"];
align?: FlexboxProps["alignItems"];
justify?: FlexboxProps["justifyContent"];
wrap?: FlexboxProps["flexWrap"];
};
type BaseFlexProps = FlexOptions & BoxProps;
- We create the FlexOptions prop to cover the system props, and finally create the
BaseFlexProps
which is a union type of FlexOptions and BoxProps. Pass theBaseFlexProps
type toBaseFlex
.
const BaseFlex = styled(Box)<BaseFlexProps>`...`;
- Now we will create our Flex component. The complete code is as follows -
import * as React from "react";
import styled from "styled-components";
import { system, FlexboxProps } from "styled-system";
import { Box, BoxProps } from "../box";
type FlexOmitted = "display";
type FlexOptions = {
direction?: FlexboxProps["flexDirection"];
align?: FlexboxProps["alignItems"];
justify?: FlexboxProps["justifyContent"];
wrap?: FlexboxProps["flexWrap"];
};
type BaseFlexProps = FlexOptions & BoxProps;
const BaseFlex = styled(Box)<BaseFlexProps>`
display: flex;
${system({
direction: {
property: "flexDirection",
},
align: {
property: "alignItems",
},
justify: {
property: "justifyContent",
},
wrap: {
property: "flexWrap",
},
})}
`;
export interface FlexProps extends Omit<BaseFlexProps, FlexOmitted> {}
export const Flex = React.forwardRef<HTMLDivElement, FlexProps>(
(props, ref) => {
const { direction = "row", children, ...delegated } = props;
return (
<BaseFlex ref={ref} direction={direction} {...delegated}>
{children}
</BaseFlex>
);
}
);
I really like this pattern of separating the styled component from the React component so that it becomes easy to intercept any props before passing on, adding default values to props, consistent types and also passing the ref.
Keep in mind we neither export the
BaseFlexProps
orBaseFlex
we exportFlexProps
andFlex
.
Spacer Component
Please check the docs and first understand how spacer works here.
In its essence it is a flexible flex component that expands along the major axis of its containing flex layout. It renders a
div
by default, and takes up any available space.If we use
Spacer
andFlex
component together, the children will span the entire width of the container and also have equal spacing between them.Paste the following code for Spacer component below the Flex component code -
type SpaceOmitted = "flex" | "justifySelf" | "alignSelf";
export interface SpacerProps extends Omit<BoxProps, SpaceOmitted> {}
export const Spacer = React.forwardRef<HTMLDivElement, SpacerProps>(
(props, ref) => {
const { children, ...delegated } = props;
return (
<Box
ref={ref}
flex="1"
justifySelf="stretch"
alignSelf="stretch"
{...delegated}
>
{children}
</Box>
);
}
);
Story
- With the above our
Flex
andSpacer
components are completed, let us create a story. - Under the
src/components/atoms/layout/flex/flex.stories.tsx
file we add the below story code. - We will create 2 stories one for the Playground for
Flex
and one forSpacer
.
import * as React from "react";
import { Box } from "../box";
import { Flex, FlexProps, Spacer } from ".";
export default {
title: "Atoms/Layout/Flex",
};
export const Playground = {
argTypes: {
direction: {
name: "direction",
type: { name: "string", required: false },
defaultValue: "row",
description: "Shorthand for flexDirection style prop",
table: {
type: { summary: "string" },
defaultValue: { summary: "row" },
},
control: {
type: "select",
options: [
"initial",
"inherit",
"unset",
"revert",
"row",
"row-reverse",
"column",
"column-reverse",
],
},
},
justify: {
name: "justify",
type: { name: "string", required: false },
defaultValue: "flex-start",
description: "Shorthand for justifyContent style prop",
table: {
type: { summary: "string" },
defaultValue: { summary: "flex-start" },
},
control: {
type: "select",
options: [
"justify-content",
"flex-start",
"flex-end",
"center",
"space-between",
"space-around",
"space-evenly",
"initial",
"inherit",
],
},
},
align: {
name: "align",
type: { name: "string", required: false },
defaultValue: "stretch",
description: "Shorthand for alignItems style prop",
table: {
type: { summary: "string" },
defaultValue: { summary: "stretch" },
},
control: {
type: "select",
options: [
"stretch",
"center",
"flex-start",
"flex-end",
"baseline",
"initial",
"inherit",
],
},
},
},
render: (args: FlexProps) => (
<Flex justify="space-between" color="white" {...args}>
<Box size="100px" bg="green500">
Box 1
</Box>
<Box size="100px" bg="blue500">
Box 2
</Box>
<Box basis="300px" size="100px" bg="tomato">
Box 3
</Box>
</Flex>
),
};
export const FlexSpacer = {
argTypes: {
direction: {
name: "direction",
type: { name: "string", required: false },
defaultValue: "row",
description: "Shorthand for flexDirection style prop",
table: {
type: { summary: "string" },
defaultValue: { summary: "row" },
},
control: {
type: "select",
options: [
"initial",
"inherit",
"unset",
"revert",
"row",
"row-reverse",
"column",
"column-reverse",
],
},
},
},
render: (args: FlexProps) => (
<Flex h="80vh" color="white" {...args}>
<Box size="100px" p="md" bg="red400">
Box 1
</Box>
<Spacer />
<Box size="100px" p="md" bg="green400">
Box 2
</Box>
</Flex>
),
};
- Now run
npm run storybook
check the stories. Under the Playground stories check the controls section play with the props, add more controls if you like.
Build the Library
- Under the
/layout/index.ts
file and paste the following -
export * from "./box";
export * from "./flex";
Now
npm run build
.Under the folder
example/src/App.tsx
we can test ourFlex
component. Copy paste the following code and runnpm run start
from theexample
directory.
import * as React from "react";
import { Box, Flex, Spacer } from "chakra-ui-clone";
export function App() {
return (
<Flex color="white">
<Box size="100px" p="md" bg="red400">
Box 1
</Box>
<Spacer />
<Box size="100px" p="md" bg="green400">
Box 2
</Box>
</Flex>
);
}
Summary
There you go guys in this tutorial we created Flex
and Spacer
components just like chakra ui and stories for them. You can find the code for this tutorial under the atom-layout-flex branch here. In the next tutorial we will create stack component. Until next time PEACE.
Top comments (0)