Introduction
Let us continue building our chakra components using styled-components & styled-system. In this tutorial we will complete cloning the Chakra UI Button component.
- I would like you to first check the [chakra docs] for button.
 - All the code for this tutorial can be found under the atom-form-button branch here.
 
Prerequisite
This is Part 2 building the Button Component. Please check the previous post for Part One. Also please check the Chakra Button Component code here, along with the theme / styles for the button here. In this we will build the actual Button Component, by bringing all the components we created in Part 1 together -
- Create a Button component.
 - Create story for the Button component.
 
Button Component
- As always let me first paste the code for you -
 
export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  (props, ref) => {
    const {
      children,
      colorScheme = "gray",
      variant = "solid",
      s = "md",
      loadingText,
      isLoading = false,
      isDisabled = false,
      spinnerPlacement = "start",
      spinner,
      rightIcon,
      leftIcon,
      iconSpacing = "0.5rem",
      ...delegated
    } = props;
    const contentProps = { rightIcon, leftIcon, iconSpacing, children };
    return (
      <BaseButton
        ref={ref}
        disabled={isDisabled || isLoading}
        colorScheme={colorScheme}
        variant={variant}
        s={s}
        {...delegated}
      >
        {isLoading && spinnerPlacement === "start" && (
          <ButtonSpinner label={loadingText} placement="start">
            {spinner}
          </ButtonSpinner>
        )}
        {isLoading ? (
          loadingText || (
            <span style={{ opacity: 0 }}>
              <ButtonContent {...contentProps} />
            </span>
          )
        ) : (
          <ButtonContent {...contentProps} />
        )}
        {isLoading && spinnerPlacement === "end" && (
          <ButtonSpinner label={loadingText} placement="end">
            {spinner}
          </ButtonSpinner>
        )}
      </BaseButton>
    );
  }
);
Again as always I would like you to check Chakra's Button docs play around with all the props, you will understand the above code auto-magically.
First and foremost we de-structure all the props, and assign default values to the props, wherever necessary.
For
isLoadingprop we will show theButtonSpinnercomponent and place it depending on the value of thespinnerPlacementprop and pass theloadingTextif we are to pass a custom loadingText.Look at this simple trick that chakra ui does it just sets opacity = 0 for the main
ButtonContentwhen isLoading = true.
Story
- With the above our 
Buttoncomponent is completed, let us create a story. - Under the 
src/components/atoms/form/button/button.stories.tsxfile we add the below story code. - We will create 2 stories - Playground and Default.
 
import * as React from "react";
import { colorSchemeOptions } from "../../../../theme/colors";
import { Stack } from "../../layout";
import { Button, ButtonProps } from "./button";
import {
  SearchIcon,
  PhoneIcon,
  EmailIcon,
  ArrowForwardIcon,
} from "../../icons";
export default {
  title: "Atoms/Form/Button",
};
export const Playground = {
  argTypes: {
    colorScheme: {
      name: "colorScheme",
      type: { name: "string", required: false },
      defaultValue: "gray",
      description: "The Color Scheme for the button",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "gray" },
      },
      control: {
        type: "select",
        options: colorSchemeOptions,
      },
    },
    s: {
      name: "s",
      type: { name: "string", required: false },
      defaultValue: "md",
      description: "Button size height width and vertical padding",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "md" },
      },
      control: {
        type: "select",
        options: ["xs", "sm", "md", "lg"],
      },
    },
    variant: {
      name: "variant",
      type: { name: "string", required: false },
      defaultValue: "solid",
      description: "Button variants",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "solid" },
      },
      control: {
        type: "select",
        options: ["link", "outline", "solid", "ghost", "unstyled"],
      },
    },
    isLoading: {
      name: "isLoading",
      type: { name: "boolean", required: false },
      defaultValue: false,
      description: "Pass the isLoading prop to show loading state.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "false" },
      },
    },
    loadingText: {
      name: "loadingText",
      type: { name: "string", required: false },
      defaultValue: "",
      description: "Prop to show a spinner and the loading text.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "-" },
      },
    },
    spinnerPlacement: {
      name: "spinnerPlacement",
      type: { name: "string", required: false },
      defaultValue: "start",
      description: `When a loadingText is present, you can change the
      placement of the spinner element to either start or end.
      It is start by default.`,
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "start" },
      },
      control: {
        type: "select",
        options: ["start", "end"],
      },
    },
    isDisabled: {
      name: "isDisabled",
      type: { name: "boolean", required: false },
      defaultValue: false,
      description: "Pass the isDisable prop to show disabled state.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "false" },
      },
    },
    isFullWidth: {
      name: "isFullWidth",
      type: { name: "boolean", required: false },
      defaultValue: false,
      description: "If true will expand full width.",
      table: {
        type: { summary: "string" },
        defaultValue: { summary: "false" },
      },
    },
  },
  render: (args: ButtonProps) => <Button {...args}>Button</Button>,
};
export const Default = {
  render: () => (
    <Stack direction="column" spacing="xl">
      <Stack spacing="lg" align="center">
        <Button colorScheme="teal" s="xs">
          Button
        </Button>
        <Button colorScheme="teal" s="sm">
          Button
        </Button>
        <Button colorScheme="teal" s="md">
          Button
        </Button>
        <Button colorScheme="teal" s="lg">
          Button
        </Button>
      </Stack>
      <Stack spacing="lg" align="center">
        <Button colorScheme="teal" variant="solid">
          Button
        </Button>
        <Button colorScheme="teal" variant="outline">
          Button
        </Button>
        <Button colorScheme="teal" variant="ghost">
          Button
        </Button>
        <Button colorScheme="teal" variant="link">
          Button
        </Button>
      </Stack>
      <Stack spacing="lg">
        <Button isLoading colorScheme="teal" variant="solid">
          Email
        </Button>
        <Button
          isLoading
          loadingText="Loading"
          colorScheme="teal"
          variant="outline"
          spinnerPlacement="start"
        >
          Submit
        </Button>
        <Button
          isLoading
          loadingText="Loading"
          colorScheme="teal"
          variant="outline"
          spinnerPlacement="end"
        >
          Continue
        </Button>
      </Stack>
      <Stack spacing="lg">
        <Button leftIcon={<EmailIcon />} colorScheme="teal" variant="solid">
          Email
        </Button>
        <Button
          rightIcon={<ArrowForwardIcon />}
          colorScheme="teal"
          variant="outline"
        >
          Call us
        </Button>
      </Stack>
    </Stack>
  ),
};
- Now run 
npm run storybookcheck 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 
/button/index.tsfile paste the following - 
export * from "./button";
- Under the 
/form/index.tsfile paste the following - 
export * from "./button";
- Under the 
/atom/index.tsfile paste the following - 
export * from "./layout";
export * from "./typography";
export * from "./feedback";
export * from "./icon";
export * from "./icons";
export * from "./form";
Now
npm run build.Under the folder
example/src/App.tsxwe can test ourButtoncomponent. Copy paste the default story code and runnpm run startfrom theexampledirectory.
Summary
There you go guys our Button component just is completed. You can find the code for this tutorial under the atom-form-button branch here. In the next tutorial we will create IconButton component. Until next time PEACE.
    
Top comments (0)