Create a highly reusable button with styled-system and styled-components.

If you have ever worked with component libraries like Chakra UI or Material UI you probably know how intuitive those libraries are. I have always wanted to create reusable components like components exposed by those libraries. Today we are going to create our own cool reusable button component😎.

First let's start by listing the functionalities that we expect from a reusable button. For me I expect to be able to customise things like colors, typography, size, spacing, layout, etc.

Let's start by installing the libraries we are going to use and give a brief description of what each of these libraries do. styled-components is a CSS-in-JS library that lets you write css in javascript that is scoped to one component. It's sort of meant to be a successor of css modules. Let's look at an example of how to use styled-components.

import styled from 'styled-components'

const Button = styled.button`
  background: transparent;
  border-radius: 3px;
  border: 2px solid palevioletred;
  color: palevioletred;
  margin: 0 1em;
  padding: 0.25em 1em;
`

Now whenever you want to use that button you just import it like a regular react component. styled-components allow you to pass props for customisations so if you for example want to change the font-size of the button based on a prop you can do it like so.

import styled, { css } from 'styled-components'

const Button = styled.button`
  background: transparent;
  border-radius: 3px;
  border: 2px solid palevioletred;
  color: palevioletred;
  margin: 0 1em;
  padding: 0.25em 1em;
  ${props => props.fontSize ? css`
          font-size: props.fontSize;             
`: ''}
`

When we want to pass a custom font size to this component you do it like so.

<Button fontSize='2rem'>My button</Button>

You can imagine how we can build dynamic components just by leveraging this API. I like this way of building components but if we add styled-system we can create even more robust components.

Let's start by defining what styled-system is before we use it. From their Docs styled system is a collection of utility functions that add style props to your React components and allows you to control styles based on a global theme object with typographic scales, colors, and layout properties. Styled system is used with a CSS-in-JS library like styled-components.

Let's look at a basic example.

import styled from 'styled-components'
import { color } from 'styled-system'

const Box = styled.div`
  ${color}
`

Now, this component will have two style props available: color to set foreground color, and bg to set background color. (You can also use backgroundColor).

<Box color="#eee" bg="orange">
  Orange
</Box>

Now that we have a basic idea of how both styled-components and the styled-system work, let's start creating our <Button/> component.

import styled from 'styled-components'
import { color } from 'styled-system'

const Button = styled.button`
    border: 0;
    outline: 0;
    ${color}
 `

This allows us to style the button like so,

<Button color="white" backgroundColor="tomato">
  Hello, world!
</Button>

Add spacing and font-sizes

import styled from 'styled-components'
import { color, space, fontSize } from 'styled-system'

const Button = styled.button`
   border: 0;
   outline: 0;
   ${color}
   ${space}
   ${fontSize}
 `

Now you can customize the padding, font size and margins. Below is an example of how we can use the button.

<Button color="white" backgroundColor="tomato" px='2rem' mr='1rem' fontSize='2rem'>
  Hello, world!
</Button>

As you can see our component is becoming more and more useful, but you probably don't want to pass all these props when you are working with this component. That is when default props and theming come to the rescue.

Lets create a basic theme with colors and pass default props to our button.

import styled, { ThemeProvider } from 'styled-components'
import { color, space, fontSize } from 'styled-system'

const theme = {
  colors: {
    custom: '#444',
    yellow: 'yellow'
  }
}

const Button = styled.button`
   border: 0;
   outline: 0;
   ${color}
   ${space}
   ${fontSize}
 `
Button.defaultProps = {
  backgroundColor: 'blue'
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Button color='custom'>Styled Button</Button>
    </ThemeProvider>
  )
}

From this code all buttons will have a blue background because we passed it as a default prop. Passing the bg or backgroundColor prop to the buttons will override the default backgroundColor prop.

For buttons we usually want to pass the variant prop to further customise the button. The buttonStyle function from styled system allows us to add a variant prop which becomes very useful if we extend our theme. Below is the code that demonstrates that.

import styled, { ThemeProvider } from 'styled-components'
import { color, space, fontSize, buttonStyle } from 'styled-system'

const theme = {
  colors: {
    custom: '#444',
    yellow: 'yellow'
  },
 buttons: {
    primary: {
      color: 'white',
      backgroundColor: 'blue'
    },
    secondary: {
      color: 'white',
      backgroundColor: 'green'
    }  
  }
}

const Button = styled.button`
   border: 0;
   outline: 0;
   ${color}
   ${space}
   ${fontSize}
   ${buttonStyle}
 `
Button.defaultProps = {
  variant: 'primary',
  backgroundColor: 'blue'
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Button color='custom' variant='secondary'>Styled Button</Button>
    </ThemeProvider>
  )
}

Adding custom props

What if you want to pass a prop like size to your buttons which may be either small, medium and large🤔? Well styled styled system allows us to do that through the variant function. Below is our final code that have all those things put together. Note that this is just a basic button, you can go even way more than this depending on your needs.

import styled, { ThemeProvider } from 'styled-components'
import { color, space, fontSize, buttonStyle, variant } from 'styled-system'

const buttonSize = variant({
  prop: 'size',
  key: 'buttonSizes'
})

const theme = {
  colors: {
    custom: '#444',
    yellow: 'yellow'
  },
 buttons: {
    primary: {
      color: 'white',
      backgroundColor: 'blue'
    },
    secondary: {
      color: 'white',
      backgroundColor: 'green'
    }  
  },
buttonSizes: {
    small: {
      fontSize: '15px',
      padding: `7px 15px`
    },
    medium: {
      fontSize: '18px',
      padding: `9px 20px`
    },
    large: {
      fontSize: '22px',
      padding: `15px 30px`
    }
  }
}

const Button = styled.button`
   border: 0;
   outline: 0;
   ${color}
   ${space}
   ${fontSize}
   ${buttonStyle}
 `
Button.defaultProps = {
  variant: 'primary',
  backgroundColor: 'blue',
  size: 'medium'
}

const App = () => {
  return (
    <ThemeProvider theme={theme}>
      <Button color='custom' variant='secondary' size='large'>Styled Button</Button>
    </ThemeProvider>
  )
}

13