DEV Community

Arsalan Ahmed Yaldram
Arsalan Ahmed Yaldram

Posted on

Build Chakra UI Spinner 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 Spinner component.

  • I would like you to first check the chakra docs for spinner.
  • We will compose (extend) our Box component to create the Spinner component and add some more styles and a size variant.
  • All the code for this tutorial can be found here under the atom-feedback-spinner branch.

Prerequisite

Please check the previous post where we have completed the Heading Component. Also please check the Chakra Spinner Component code here. Check the theme and styles for the Spinner Component here.

In this tutorial we will -

  • Create a Spinner component.

Setup

  • First let us create a branch, from the main branch run -
git checkout -b atom-feedback-spinner
Enter fullscreen mode Exit fullscreen mode
  • Under the components/atoms folder create a new folder called feedback. Under feedback folder create 2 files index.ts and spinner.tsx.

  • So our folder structure stands like - src/components/atoms/feedback.

Spinner Component

  • If you did read and try out chakra's Spinner component you know what we need here. We need to handle the size of the Spinner, the color, its speed and also an emptyColor property.

  • Let me paste the code -

import * as React from "react";
import styled, { css, keyframes } from "styled-components";
import { variant as variantFun, ResponsiveValue } from "styled-system";

import { Box, BoxProps } from "../layout";
import { VisuallyHiddenText } from "../typography";

const spin = keyframes`
 0% {
    transform: rotate(0deg);
  }

  100% {
    transform: rotate(360deg);
  }
`;

type SpinnerSize = "xs" | "sm" | "md" | "lg" | "xl";

interface SpinnerOptions {
  emptyColor?: string;
  thickness?: string;
  speed?: string;
  label?: string;
  variant?: ResponsiveValue<SpinnerSize>;
}

export interface SpinnerProps
  extends Omit<BoxProps, keyof SpinnerOptions>,
    SpinnerOptions {}

const animation = (speed = "0.45s") => css`
  ${spin} ${speed} linear infinite;
`;
const BaseSpinner = styled(Box)<SpinnerProps>`
  animation: ${({ speed }) => animation(speed)};

  width: 1em;
  height: 1em;

  ${variantFun({
    prop: "variant",
    variants: {
      xs: {
        width: "1.5rem",
        height: "1.5rem",
      },
      sm: {
        width: "2rem",
        height: "2rem",
      },
      md: {
        width: "2.5rem",
        height: "2.5rem",
      },
      lg: {
        width: "3rem",
        height: "3rem",
      },
      xl: {
        width: "3.5rem",
        height: "3.5rem",
      },
    },
  })}
`;

export const Spinner = React.forwardRef<HTMLDivElement, SpinnerProps>(
  (props, ref) => {
    const {
      label = "Loading....",
      thickness = "2px",
      emptyColor = "transparent",
      color,
      ...delegated
    } = props;

    const spinnerStyles = {
      display: "inline-block",
      borderColor: "currentColor",
      borderStyle: "solid",
      borderRadius: "99999px",
      borderWidth: thickness,
      borderBottomColor: emptyColor,
      borderLeftColor: emptyColor,
      color,
    };

    return (
      <BaseSpinner ref={ref} {...spinnerStyles} {...delegated}>
        {label && <VisuallyHiddenText>{label}</VisuallyHiddenText>}
      </BaseSpinner>
    );
  }
);
Enter fullscreen mode Exit fullscreen mode
  • First thing to notice, we are using the animation function from styled-components. We are passing in the speed parameter, which we can now control via props.

  • Second thing to notice, we introduced a prop called variant by which we control the size of the Spinner component. The prop is named variant so that it does not conflict with the styled-system size prop.

  • Third the use of VisuallyHiddenText which we created in the Text component tutorial. It will be used by screen readers, pass some meaningful accessible text using the label prop.

  • Fourth thing, we are passing the color prop and using the CSS "currentColor" property, this will make the borderColor = color.

Build the Library

  • Under the /feedback/index.ts file and paste the following -
export * from "./spinner";
Enter fullscreen mode Exit fullscreen mode
  • Under the /atom/index.ts file and paste the following -
export * from "./layout";
export * from "./typography";
export * from "./feedback";
Enter fullscreen mode Exit fullscreen mode
  • Now npm run build.

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

import * as React from "react";
import { Stack, Spinner } from "chakra-ui-clone";

export function App() {
  return (
    <Stack spacing="lg">
      <Spinner variant="xs" />
      <Spinner variant="sm" />
      <Spinner variant="md" />
      <Spinner variant="lg" />
      <Spinner variant="xl" />
      <Spinner variant="sm" color="red500" />
      <Spinner
        thickness="4px"
        speed="0.65s"
        emptyColor="gray200"
        color="blue500"
        variant="xl"
      />
    </Stack>
  );
}
Enter fullscreen mode Exit fullscreen mode

Summary

There you go guys in this tutorial we created Spinner component just like chakra ui. You can find the code for this tutorial under the atom-feedback-spinner branch here. In the next tutorial we will create Icon component. Until next time PEACE.

Discussion (0)