Reviews of UI libraries. Part 1: Chakra UI

UI libraries are a hot topic for Frontend in 2022, there are several popular ones and people have their own thoughts about which one is the best one for their needs. In this post we will go over the most popular ones.

I will not cover Bootstrap or Material Design, reason is that they don’t give enough versatility to be a viable choice. They’re great if you don’t have a designer, but its very common that a designer or the clients gives you a wireframe that is hard to reproduce using those two libraries, making you fight the library more than it helps you.

I will consider the following rating system from 1-10:

Chakra UI.

Chakra UI is a simple, modular and accessible component library that gives you the building blocks you need to build your React applications.

Among the community, this is probably the most popular choice, it gives you a lot of tools to create awesome applications.

Its characteristics:

Sample Code:

import * as React from "react";
import { Box, Center, Image, Flex, Badge, Text } from "@chakra-ui/react";
import { MdStar } from "react-icons/md";

export default function Example() {
  return (
    <Center h="100vh">
      <Box p="5" maxW="320px" borderWidth="1px">
        <Image borderRadius="md" src="https://bit.ly/2k1H1t6" />
        <Flex align="baseline" mt={2}>
          <Badge colorScheme="pink">Plus</Badge>
          <Text
            ml={2}
            textTransform="uppercase"
            fontSize="sm"
            fontWeight="bold"
            color="pink.800"
          >
            Verified • Cape Town
          </Text>
        </Flex>
        <Text mt={2} fontSize="xl" fontWeight="semibold" lineHeight="short">
          Modern, Chic Penthouse with Mountain, City & Sea Views
        </Text>
        <Text mt={2}>$119/night</Text>
        <Flex mt={2} align="center">
          <Box as={MdStar} color="orange.400" />
          <Text ml={1} fontSize="sm">
            <b>4.84</b> (190)
          </Text>
        </Flex>
      </Box>
    </Center>
  );
}

Check the sandbox here.

If you were to reproduce this with HTML/CSS it would be pretty much the same order, also, the props are very easy to understand, like mt={2} is margin-top: 2rem.

A few observations though:

Going through the documentation, it provides step by step guides for CRA, Next, Gatsby, Blitz, Redwood, Remix and Vite.

Checking the Vite installation instruction:

npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6

It has dependencies with emotion, framer-motion and styled, which in my opinion, is too bloated.

Layout

Aspect Ratio

For a square video, we assign a ratio of 1.

// This video will have equal sides
<AspectRatio maxW="560px" ratio={1}>
  <iframe
    title="naruto"
    src="https://www.youtube.com/embed/QhBnZ6NPOY0"
    allowFullScreen
  />
</AspectRatio>

For a 16 / 9:

<AspectRatio maxW="400px" ratio={16 / 9}>
  <Image src="https://bit.ly/naruto-sage" alt="naruto" objectFit="cover" />
</AspectRatio>

Box

<Box maxW="sm" borderWidth="1px" borderRadius="lg" overflow="hidden">
  <Box bg="tomato" w="100%" p={4} color="white">
    This is the Box
  </Box>
  <Box display="flex" alignItems="baseline">
    <Badge borderRadius="full" px="2" colorScheme="teal">
      New
    </Badge>
    <Badge borderRadius="full" px="2" colorScheme="teal">
      Old
    </Badge>
  </Box>
</Box>

Center

Put any child element inside it, give it any width or/and height, it’ll ensure the child is centered.

 <Center w='40px' h='40px' bg='tomato' color='white'>
    <PhoneIcon />
  </Center>
  <Center w='40px' h='40px' bg='tomato' color='white'>
    <Box as='span' fontWeight='bold' fontSize='lg'>
      1
    </Box>
  </Center>

Container

Containers are used to constrain a content’s width to the current breakpoint, while keeping it fluid.

<Container maxW="2xl" bg="blue.600" centerContent>
  <Box padding="4" bg="blue.400" color="black" maxW="md">
    There are many benefits to a joint design and development system. Not only
    does it bring benefits to the design team, but it also brings benefits to
    engineering teams. It makes sure that our experiences have a consistent look
    and feel, not just in our design specs, but in production.
  </Box>
</Container>

Flex

Flex is Box with display: flex and comes with helpful style shorthand. It renders a div element.

<Flex>
  <Box p="4" bg="red.400">
    Box 1
  </Box>
  <Spacer />
  <Box p="4" bg="green.400">
    Box 2
  </Box>
</Flex>

Its props are align, basis, direction, grow, justify, shrink, wrap.

Grid

Grid is Box with display: grid and it comes with helpful style shorthand. It renders a div element.

<Grid
  h="200px"
  templateRows="repeat(2, 1fr)"
  templateColumns="repeat(5, 1fr)"
  gap={4}
>
  <GridItem rowSpan={2} colSpan={1} bg="tomato" />
  <GridItem colSpan={2} bg="papayawhip" />
  <GridItem colSpan={2} bg="papayawhip" />
  <GridItem colSpan={4} bg="tomato" />
</Grid>

Simple Grid

SimpleGrid provides a friendly interface to create responsive grid layouts with ease. It renders a div element with display: grid.

<SimpleGrid columns={[2, null, 3]} spacing="40px">
  <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>

Stack

Stack is a layout component that makes it easy to stack elements together and apply a space between them. It uses a modified version of the CSS lobotomized owl selector to add spacing between its children.

<HStack spacing="24px">
  <Box w="40px" h="40px" bg="yellow.200">
    1
  </Box>
  <Box w="40px" h="40px" bg="tomato">
    2
  </Box>
  <Box w="40px" h="40px" bg="pink.100">
    3
  </Box>
</HStack>

Wrap

Wrap is a layout component that adds a defined space between its children. It wraps its children automatically if there isn’t enough space to fit any more in the same row.

<Wrap>
  <WrapItem>
    <Center w="180px" h="80px" bg="red.200">
      Box 1
    </Center>
  </WrapItem>
  <WrapItem>
    <Center w="180px" h="80px" bg="green.200">
      Box 2
    </Center>
  </WrapItem>
  <WrapItem>
    <Center w="180px" h="80px" bg="tomato">
      Box 3
    </Center>
  </WrapItem>
  <WrapItem>
    <Center w="180px" h="80px" bg="blue.200">
      Box 4
    </Center>
  </WrapItem>
</Wrap>

Forms

Button

<Stack spacing={4} direction="row" align="center">
  <Button colorScheme="teal" size="xs">
    Button
  </Button>
  <Button colorScheme="teal" size="sm">
    Button
  </Button>
  <Button colorScheme="teal" size="md">
    Button
  </Button>
  <Button colorScheme="teal" size="lg">
    Button
  </Button>
</Stack>

Checkbox

<Checkbox defaultChecked>Checkbox</Checkbox>

Editable

Editable Text is used for inline renaming of some text. It appears as normal UI text but transforms into a text input field when the user clicks on or focuses it.

// Click the text to edit
<Editable defaultValue="Take some chakra">
  <EditablePreview />
  <EditableInput />
</Editable>

Form Control

https://chakra-ui.com/docs/components/form/form-control

Icon Button

<IconButton
  colorScheme="blue"
  aria-label="Search database"
  icon={<SearchIcon />}
/>

Input

<Stack spacing={3}>
  <Input placeholder="extra small size" size="xs" />
  <Input placeholder="small size" size="sm" />
  <Input placeholder="medium size" size="md" />
  <Input placeholder="large size" size="lg" />
</Stack>

Radio

<RadioGroup defaultValue="2">
  <Stack spacing={5} direction="row">
    <Radio colorScheme="red" value="1">
      Radio
    </Radio>
    <Radio colorScheme="green" value="2">
      Radio
    </Radio>
  </Stack>
</RadioGroup>

Range Slider

<RangeSlider aria-label={["min", "max"]} defaultValue={[10, 30]}>
  <RangeSliderTrack>
    <RangeSliderFilledTrack />
  </RangeSliderTrack>
  <RangeSliderThumb index={0} />
  <RangeSliderThumb index={1} />
</RangeSlider>

Select

<Stack spacing={3}>
  <Select placeholder="extra small size" size="xs" />
  <Select placeholder="small size" size="sm" />
  <Select placeholder="medium size" size="md" />
  <Select placeholder="large size" size="lg" />
</Stack>

Slider

<Slider aria-label="slider-ex-1" defaultValue={30}>
  <SliderTrack>
    <SliderFilledTrack />
  </SliderTrack>
  <SliderThumb />
</Slider>

Switch

<Stack align="center" direction="row">
  <Switch size="sm" />
  <Switch size="md" />
  <Switch size="lg" />
</Stack>

Textarea

<Textarea placeholder="Here is a sample placeholder" />

Data Display

Badge

<Stack direction="row">
  <Badge variant="outline" colorScheme="green">
    Default
  </Badge>
  <Badge variant="solid" colorScheme="green">
    Success
  </Badge>
  <Badge variant="subtle" colorScheme="green">
    Removed
  </Badge>
</Stack>

Code

<Stack direction="row">
  <Code children="console.log(welcome)" />
  <Code colorScheme="red" children="var chakra = 'awesome!'" />
  <Code colorScheme="yellow" children="npm install chakra" />
</Stack>

Divider

<Divider orientation="horizontal" />
<Center height="50px">
  <Divider orientation="vertical" />
</Center>

Kbd

<span>
  <Kbd>shift</Kbd> + <Kbd>H</Kbd>
</span>

List

<UnorderedList>
  <ListItem>Lorem ipsum dolor sit amet</ListItem>
  <ListItem>Consectetur adipiscing elit</ListItem>
  <ListItem>Integer molestie lorem at massa</ListItem>
  <ListItem>Facilisis in pretium nisl aliquet</ListItem>
</UnorderedList>

Stat

<StatGroup>
  <Stat>
    <StatLabel>Sent</StatLabel>
    <StatNumber>345,670</StatNumber>
    <StatHelpText>
      <StatArrow type="increase" />
      23.36%
    </StatHelpText>
  </Stat>

  <Stat>
    <StatLabel>Clicked</StatLabel>
    <StatNumber>45</StatNumber>
    <StatHelpText>
      <StatArrow type="decrease" />
      9.05%
    </StatHelpText>
  </Stat>
</StatGroup>

Table

<TableContainer>
  <Table variant="simple">
    <TableCaption>Imperial to metric conversion factors</TableCaption>
    <Thead>
      <Tr>
        <Th>To convert</Th>
        <Th>into</Th>
        <Th isNumeric>multiply by</Th>
      </Tr>
    </Thead>
    <Tbody>
      <Tr>
        <Td>inches</Td>
        <Td>millimetres (mm)</Td>
        <Td isNumeric>25.4</Td>
      </Tr>
      <Tr>
        <Td>feet</Td>
        <Td>centimetres (cm)</Td>
        <Td isNumeric>30.48</Td>
      </Tr>
      <Tr>
        <Td>yards</Td>
        <Td>metres (m)</Td>
        <Td isNumeric>0.91444</Td>
      </Tr>
    </Tbody>
    <Tfoot>
      <Tr>
        <Th>To convert</Th>
        <Th>into</Th>
        <Th isNumeric>multiply by</Th>
      </Tr>
    </Tfoot>
  </Table>
</TableContainer>

Tag

<HStack spacing={4}>
  {["sm", "md", "lg"].map((size) => (
    <Tag size={size} key={size} variant="solid" colorScheme="teal">
      Teal
    </Tag>
  ))}
</HStack>

Feedback

Alert

<Stack spacing={3}>
  <Alert status="error">
    <AlertIcon />
    There was an error processing your request
  </Alert>

  <Alert status="success">
    <AlertIcon />
    Data uploaded to the server. Fire on!
  </Alert>

  <Alert status="warning">
    <AlertIcon />
    Seems your account is about expire, upgrade now
  </Alert>

  <Alert status="info">
    <AlertIcon />
    Chakra is going live on August 30th. Get ready!
  </Alert>
</Stack>

Circular Progress

<CircularProgress value={40} color="green.400">
  <CircularProgressLabel>40%</CircularProgressLabel>
</CircularProgress>

Progress

<Progress value={80} />

Skeleton

<Stack>
  <Skeleton height="20px" />
  <Skeleton height="20px" />
  <Skeleton height="20px" />
</Stack>

Or

<Box padding="6" boxShadow="lg" bg="white">
  <SkeletonCircle size="10" />
  <SkeletonText mt="4" noOfLines={4} spacing="4" />
</Box>

Spinner

<Stack direction="row" spacing={4}>
  <Spinner size="xs" />
  <Spinner size="sm" />
  <Spinner size="md" />
  <Spinner size="lg" />
  <Spinner size="xl" />
  <Spinner
    thickness="4px"
    speed="0.65s"
    emptyColor="gray.200"
    color="blue.500"
    size="xl"
  />
</Stack>

Toast

function ToastExample() {
  const toast = useToast();
  return (
    <Button
      onClick={() =>
        toast({
          title: "Account created.",
          description: "We've created your account for you.",
          status: "success",
          duration: 9000,
          isClosable: true,
        })
      }
    >
      Show Toast
    </Button>
  );
}

Typography

Text

<>
  <Text as="i">Italic</Text>
  <br />
  <Text as="u">Underline</Text>
  <br />
  <Text as="abbr">I18N</Text>
  <br />
  <Text as="cite">Citation</Text>
  <br />
  <Text as="del">Deleted</Text>
  <br />
  <Text as="em">Emphasis</Text>
  <br />
  <Text as="ins">Inserted</Text>
  <br />
  <Text as="kbd">Ctrl + C</Text>
  <br />
  <Text as="mark">Highlighted</Text>
  <br />
  <Text as="s">Strikethrough</Text>
  <br />
  <Text as="samp">Sample</Text>
  <br />
  <Text as="sub">sub</Text>
  <br />
  <Text as="sup">sup</Text>
</>

Heading

<Stack spacing={6}>
  <Heading as="h1" size="4xl" isTruncated>
    (4xl) In love with React & Next
  </Heading>
  <Heading as="h2" size="3xl" isTruncated>
    (3xl) In love with React & Next
  </Heading>
  <Heading as="h2" size="2xl">
    (2xl) In love with React & Next
  </Heading>
  <Heading as="h2" size="xl">
    (xl) In love with React & Next
  </Heading>
  <Heading as="h3" size="lg">
    (lg) In love with React & Next
  </Heading>
  <Heading as="h4" size="md">
    (md) In love with React & Next
  </Heading>
  <Heading as="h5" size="sm">
    (sm) In love with React & Next
  </Heading>
  <Heading as="h6" size="xs">
    (xs) In love with React & Next
  </Heading>
</Stack>

Overlay

Alert Dialog

function AlertDialogExample() {
  const { isOpen, onOpen, onClose } = useDisclosure();
  const cancelRef = React.useRef();

  return (
    <>
      <Button colorScheme="red" onClick={onOpen}>
        Delete Customer
      </Button>

      <AlertDialog
        isOpen={isOpen}
        leastDestructiveRef={cancelRef}
        onClose={onClose}
      >
        <AlertDialogOverlay>
          <AlertDialogContent>
            <AlertDialogHeader fontSize="lg" fontWeight="bold">
              Delete Customer
            </AlertDialogHeader>

            <AlertDialogBody>
              Are you sure? You can't undo this action afterwards.
            </AlertDialogBody>

            <AlertDialogFooter>
              <Button ref={cancelRef} onClick={onClose}>
                Cancel
              </Button>
              <Button colorScheme="red" onClick={onClose} ml={3}>
                Delete
              </Button>
            </AlertDialogFooter>
          </AlertDialogContent>
        </AlertDialogOverlay>
      </AlertDialog>
    </>
  );
}

Drawer

function DrawerExample() {
  const { isOpen, onOpen, onClose } = useDisclosure();
  const btnRef = React.useRef();

  return (
    <>
      <Button ref={btnRef} colorScheme="teal" onClick={onOpen}>
        Open
      </Button>
      <Drawer
        isOpen={isOpen}
        placement="right"
        onClose={onClose}
        finalFocusRef={btnRef}
      >
        <DrawerOverlay />
        <DrawerContent>
          <DrawerCloseButton />
          <DrawerHeader>Create your account</DrawerHeader>

          <DrawerBody>
            <Input placeholder="Type here..." />
          </DrawerBody>

          <DrawerFooter>
            <Button variant="outline" mr={3} onClick={onClose}>
              Cancel
            </Button>
            <Button colorScheme="blue">Save</Button>
          </DrawerFooter>
        </DrawerContent>
      </Drawer>
    </>
  );
}
<Menu>
  <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
    Actions
  </MenuButton>
  <MenuList>
    <MenuItem>Download</MenuItem>
    <MenuItem>Create a Copy</MenuItem>
    <MenuItem>Mark as Draft</MenuItem>
    <MenuItem>Delete</MenuItem>
    <MenuItem>Attend a Workshop</MenuItem>
  </MenuList>
</Menu>
function BasicUsage() {
  const { isOpen, onOpen, onClose } = useDisclosure();
  return (
    <>
      <Button onClick={onOpen}>Open Modal</Button>

      <Modal isOpen={isOpen} onClose={onClose}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modal Title</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Lorem count={2} />
          </ModalBody>

          <ModalFooter>
            <Button colorScheme="blue" mr={3} onClick={onClose}>
              Close
            </Button>
            <Button variant="ghost">Secondary Action</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
    </>
  );
}

Popover

<Popover>
  <PopoverTrigger>
    <Button>Trigger</Button>
  </PopoverTrigger>
  <PopoverContent>
    <PopoverArrow />
    <PopoverCloseButton />
    <PopoverHeader>Confirmation!</PopoverHeader>
    <PopoverBody>Are you sure you want to have that milkshake?</PopoverBody>
  </PopoverContent>
</Popover>

Tooltip

<Tooltip label="Hey, I'm here!" aria-label="A tooltip">
  Hover me
</Tooltip>

Disclosure

Accordion

<Accordion defaultIndex={[0]} allowMultiple>
  <AccordionItem>
    <h2>
      <AccordionButton>
        <Box flex="1" textAlign="left">
          Section 1 title
        </Box>
        <AccordionIcon />
      </AccordionButton>
    </h2>
    <AccordionPanel pb={4}>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat.
    </AccordionPanel>
  </AccordionItem>

  <AccordionItem>
    <h2>
      <AccordionButton>
        <Box flex="1" textAlign="left">
          Section 2 title
        </Box>
        <AccordionIcon />
      </AccordionButton>
    </h2>
    <AccordionPanel pb={4}>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat.
    </AccordionPanel>
  </AccordionItem>
</Accordion>

Tabs

<Tabs>
  <TabList>
    <Tab>One</Tab>
    <Tab>Two</Tab>
    <Tab>Three</Tab>
  </TabList>

  <TabPanels>
    <TabPanel>
      <p>one!</p>
    </TabPanel>
    <TabPanel>
      <p>two!</p>
    </TabPanel>
    <TabPanel>
      <p>three!</p>
    </TabPanel>
  </TabPanels>
</Tabs>

Visually Hidden

function Example() {
  return (
    <Box>
      <Heading>Title and description</Heading>
      <VisuallyHidden>This will be hidden</VisuallyHidden>
    </Box>
  );
}
<Breadcrumb>
  <BreadcrumbItem>
    <BreadcrumbLink href="#">Home</BreadcrumbLink>
  </BreadcrumbItem>

  <BreadcrumbItem>
    <BreadcrumbLink href="#">Docs</BreadcrumbLink>
  </BreadcrumbItem>

  <BreadcrumbItem isCurrentPage>
    <BreadcrumbLink href="#">Breadcrumb</BreadcrumbLink>
  </BreadcrumbItem>
</Breadcrumb>
<Link href="https://chakra-ui.com" isExternal>
  Chakra Design system <ExternalLinkIcon mx="2px" />
</Link>
<LinkBox as="article" maxW="sm" p="5" borderWidth="1px" rounded="md">
  <Box as="time" dateTime="2021-01-15 15:30:00 +0000 UTC">
    13 days ago
  </Box>
  <Heading size="md" my="2">
    <LinkOverlay href="#">
      New Year, New Beginnings: Smashing Workshops & Audits
    </LinkOverlay>
  </Heading>
  <Text mb="3">
    Catch up on what’s been cookin’ at Smashing and explore some of the most
    popular community resources.
  </Text>
  <Box as="a" color="teal.400" href="#" fontWeight="bold">
    Some inner link
  </Box>
</LinkBox>

Media and Icons

Avatar

<Wrap>
  <WrapItem>
    <Avatar name="Dan Abrahmov" src="https://bit.ly/dan-abramov" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Kola Tioluwani" src="https://bit.ly/tioluwani-kolawole" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Kent Dodds" src="https://bit.ly/kent-c-dodds" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Ryan Florence" src="https://bit.ly/ryan-florence" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Prosper Otemuyiwa" src="https://bit.ly/prosper-baba" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Christian Nwamba" src="https://bit.ly/code-beast" />
  </WrapItem>
  <WrapItem>
    <Avatar name="Segun Adebayo" src="https://bit.ly/sage-adebayo" />
  </WrapItem>
</Wrap>

Icon

import { PhoneIcon, AddIcon, WarningIcon } from '@chakra-ui/icons'

// The default icon size is 1em (16px)
<PhoneIcon />

// Use the `boxSize` prop to change the icon size
<AddIcon w={6} h={6} />

// Use the `color` prop to change the icon color
<WarningIcon w={8} h={8} color="red.500" />

Image

<Stack direction="row">
  <Image
    boxSize="100px"
    objectFit="cover"
    src="https://bit.ly/dan-abramov"
    alt="Dan Abramov"
  />
  <Image
    boxSize="150px"
    objectFit="cover"
    src="https://bit.ly/dan-abramov"
    alt="Dan Abramov"
  />
  <Image boxSize="200px" src="https://bit.ly/dan-abramov" alt="Dan Abramov" />
</Stack>

Other

Close Button

<CloseButton />

Transitions

function FadeEx() {
  const { isOpen, onToggle } = useDisclosure();

  return (
    <>
      <Button onClick={onToggle}>Click Me</Button>
      <Fade in={isOpen}>
        <Box
          p="40px"
          color="white"
          mt="4"
          bg="teal.500"
          rounded="md"
          shadow="md"
        >
          Fade
        </Box>
      </Fade>
    </>
  );
}

Rating

Conclusion

Chakra UI is very developer friendly, but overall I dislike the looks of the components and customizing them can be a bit of a pain if you wish to do something different than what the component is intended to do.

My focus is on a day to day work, so in this case, you be working with designers who are very set in their work and on a design which is agreed on the client, and even then, said client might want a specific feature this library won’t help us. I have seen this happen many time.

From a developer POV, this library is awesome, its simplifies a lot of annoying setups or thinking about the styling, but from a work POV, this library is a no for me.

As a developer you will need freedom and versatility.

See you on the next post.

Sincerely,

Eng. Adrian Beria