DEV Community

loading...
Cover image for Create a highly reusable button with styled-system and styled-components.

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

Joseph Mukorivo
Software Engineer | Python and JavaScript Developer | Writer | DevOps Enthusiast
・4 min read

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;
`
Enter fullscreen mode Exit fullscreen mode

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;             
`: ''}
`
Enter fullscreen mode Exit fullscreen mode

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

<Button fontSize='2rem'>My button</Button>
Enter fullscreen mode Exit fullscreen mode

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}
`
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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}
 `
Enter fullscreen mode Exit fullscreen mode

This allows us to style the button like so,

<Button color="white" backgroundColor="tomato">
  Hello, world!
</Button>
Enter fullscreen mode Exit fullscreen mode

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}
 `
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

Discussion (3)

Collapse
papayankey profile image
Benneth Yankey • Edited

Very nice, but part of the reusable component design lives in the theme config..😔

Check out stitches js being built by modulz. It utilizes styled-system and styled-component under the hood to provide first class support for variant, compound variant, etc all right within the component itself. It will blow you away!!

E.g

const {styled} = "@stitches/react";

const Button = styled("button", {
    // base styles,

    variants: {
       variant: {},
       size: {}
     },

    compoundVariants: [
       {
           variant: // value,
           size: // value,
           css: {} // apply styles
       },
          // other compound variants
    ],

    defaultVariants: {
         variant: // value,
         size: // value
    }
});
Enter fullscreen mode Exit fullscreen mode

Hope it helps.

Collapse
jdmg94 profile image
José Muñoz

I love styled-system, too bad they’ve abandoned the project

Collapse
horstcredible profile image
Horstcredible

You can adopt Theme UI, though.
Same API, having 5 project owners instead of just 1 as in Styled System.

So chances are high the project will not get abandoned without telling the community about it.

It also has a pretty active community.