When I finished playing around with building a Design System(or part of it) for a project, before start coding one important question popped up: Which library should I use to style my components?
Lately, I've been working with styled-components but I wanted to try the trending ones right now: Tailwind CSS or Chakra-Ui.
After watching some videos and seeing how both looked like in code, I decided to go with Chakra-Ui.
So, in this article I'm going to share what my experience have been so far with Chakra-Ui after working with it during these last 2 days. Hopefully it can help people having their first steps with the library.
1. Creating a custom theme is a breeze
By default, Chakra-Ui already comes with a theme but we can customize it to best fit our design. And that was where I started to play with Chakra since I had created a design system.
The theme object is where we define the application's color pallete, type scale, font stacks, border radius values and etc. All Chakra components inherit from this default theme.
From the default theme, we can extend and overide tokens and also add new values to the theme. Customizing the it is as easy as:
1) Extending it with extendTheme
:
import { extendTheme } from '@chakra-ui/react'
const customTheme = extendTheme({
colors: {
lightGray: {
default: '#C4C4C4',
hover: '#EEEEEE',
disabled: '#9E9E9E'
}
},
// I'm just adding one more fontSize than the default ones
fontSizes: {
xxs: '0.625rem'
},
// I'm creating a new space tokens since the default is represented with numbers
space: {
xs: '0.25rem',
sm: '0.5rem',
md: '1rem',
lg: '1.5rem',
xl: '2rem',
xxl: '3rem',
}
})
export default customTheme
2) Passing to the ChakraProvider
:
import customTheme from './theme'
<ChakraProvider theme={customTheme}>
<App />
</ChakraProvider>
3) Using it:
import customTheme from './theme'
const BoxWithText= ({ text }) => (
<Box padding='xs' borderRadius='lg'>
<Text>{text}</Text>
</Box>
)
2. Creating variants of components makes it easier to implement a design system
Besides customizing theme tokens we can also customize component styles.
Chakra component styles have a specific API that a component style consists of:
-
baseStyle
, the default style of a component -
sizes
, represents styles for different sizes of a component -
variants
, represents styles for different visual variants -
defaultProps
, optional, to define the defaultsize
orvariant
.
From the docs, what the component style looks like:
const ComponentStyle = {
// style object for base or default style
baseStyle: {},
// styles for different sizes ("sm", "md", "lg")
sizes: {},
// styles for different visual variants ("outline", "solid")
variants: {},
// default values for `size` and `variant`
defaultProps: {
size: "",
variant: "",
},
}
With the possibility of customizing each component we can create variants for them to match pre-defined styles of a component. For example, in a design system you may have different variations of the typography to show different font sizes, font weights, etc. The same goes with components such as buttons, inputs, etc.
With variants we can create pre-defined styles for those components:
import { extendTheme } from '@chakra-ui/react'
const customTheme = extendTheme({
components: {
Heading: {
variants: {
h1: {
fontSize: '4xl', fontWeight: 'bold'
},
h2: {
fontSize: '3xl', fontWeight: 'bold'
}
}
},
Text: {
variants: {
subtitle: {
fontSize: 'xl', fontWeight: 'medium'
},
body: {
fontSize: 'md', fontWeight: 'medium'
}
}
}
}
})
export default customTheme
And use it in our code:
const Badge = ({ text }) => (
<Box padding='xs' borderRadius='lg' w='max-content'>
<Text variant='bodyExtraSmall'>{text}</Text>
</Box>
)
3. Integrating with Storybook is not so smooth currently
One pain point I had with this begining of my journey with Chakra-Ui was trying to use Storybook to show my created components. For my workflow, I always create the components and their corresponding stories to be easier to see the different styles and create a component library.
However, when I created the stories with my Chakra components and checked the Storybook it did not load any styling I made with Chakra. I was frustrated at first but thanks to an issue raised I could get it working.
To fix that you can:
1) Modify the main.js
file inside the .storybook
folder to match the webpackFinal
config that Chakra uses:
const path = require("path");
const toPath = (_path) => path.join(process.cwd(), _path);
module.exports = {
stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/preset-create-react-app",
],
webpackFinal: async (config) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
"@emotion/core": toPath("node_modules/@emotion/react"),
"emotion-theming": toPath("node_modules/@emotion/react"),
},
},
};
},
};
2) Wrap the story decorator with the ChakraProvider
in the preview.js
file:
import React from "react"
import { ChakraProvider } from '@chakra-ui/react'
import theme from '../src/theme'
export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
}
const withChakra = (StoryFn) => {
return (
<ChakraProvider theme={theme}>
<StoryFn />
</ChakraProvider>
)
}
export const decorators = [withChakra]
This is a temporary workaround that I believe can be resolved any time soon since they are already working on it :)
4. We can't create variants for Box but we can use Layer Styles or Text Styles
Another "problem" I had was when I tried to create variants for the Box
component. I wanted to create different types of Badges
that I could simply choose the variant
when inserting them on my components. My Badge consisted of a Box
with a Text
inside, the code I've shown in the previous snippets.
However, after finding this issue I understood that by design Box
cannot receive variants in theme since it is a generic component, it represents a div
.
To work around that, you could use the useStyleConfig
or use the textStyle
or layerStyle
props, docs here.
Both props are used to avoid repeating specific text and layer properties and help us keep our components organized and consistent. They allow us to save styling attributes to re-use in other components, passing the corresponding prop to the component.
Since I only needed to change the background color and the border depending on the type of Badge
I wanted, I used the layer style.
To solve this:
1) Extend the theme with new layerStyles
:
const customTheme = extendTheme({
layerStyles: {
defaultBadge: { bg:'lightGray.default' },
outlinedBadge: { bg:'transparent', border: '1px solid #000000' },
whiteBadge: { bg:'#FFFFFF' }
}
})
2) Consume it in the component:
const Badge = ({ text }) => (
<Box layerStyle=`outlinedBadge` padding='xs' borderRadius='lg' w='max-content'>
<Text variant='bodyExtraSmall'>{text}</Text>
</Box>
)
Conclusion
That's it for now. I hope you could get some tips when also starting your journey with Chakra-Ui. If I find more interesting points and learnings to share I may create another article as well :)
If it was somehow useful, leave it a ❤️ or if you have more to add drop a comment.
Also, I'd love if we connect on Twitter as well :)
Top comments (17)
nice post, thanks for writing this together Carlos! 🙏
Thanks! Great that you liked it, Dominik :)
Thanks for posting this! I've been using Chakra UI for awhile now and LOVE it. Thought I'd give Storybook a go and well, because you wrote this, you know the initial outcome! It wasn't pretty. I searched for 'storybook js chakra ui' and this post came up. You're a rock star, my friend! 😀
Love this post, have a lot of experiences in this, thank you!
Thanks for the feedback, Harry! Glad that you loved it :)
Thanks a lot .
Thanks for the comment, Abderrahmane! I'm glad that it helped you :)
Thanks for the post. I'm bookmarking it for the time I'll try Chakra-UI.
Thanks, Magda! Hopefully it'll be helpful to you :)
Oi Carlos muito obrigado pelo post :)
Carlos, I was wondering if you had to create multi-part components. I'm trying to get it working but I'm not sure of the approach.
Massa que você gostou, Hélio! 🙂
Unfortunatelly I haven't used it yet. I tried to look in the docs and tried to find some other resources but I couldn't quite understand its use so I can't help you right now :( If I ended up using it and getting the grip of it I will share with you :)
Yeah, I think, the docs lack quite some information I've tried to get help from the Discord channel but without look either. I'll keep trying to find info and maybe I'll write a post just like yours which is really good :)
Obrigado, abraço
Great, Helio! It would be a massive help if you share it after you understand how it works. There are certainly other devs with the same struggle.
I'm looking forward to reading your post :) Valeuzão :D
Tanks sir
Tanks sir
Thank you man - perfect post. Especially the part with Storybook. You saved me a lot of time.
Amazing post! Thanks for share it with us