Style Hooks

Create a style hook

In this tutorial we'll go through the process of creating a style hook from scratch

import {css} from '@emotion/core'
import {useStyles} from '@style-hooks/core'
styles = {width: value => css`width: ${value}`},
useBox = props => useStyles('box', styles, props)

1. Understanding useStyles(name, styles, props)

The useStyles() hook takes three arguments, name, styles, and props. It returns a copy of props without any props that were consumed by the hook.

  • name <string> required

    This is the key name you'll use for this hook when configuring it in your theme. As such, it should be unique. Names are important because they allow your hook to use powerful features like the kind prop anddefaultProps in your theme. They also provide organization to your theme allowing you to provide a name argument to useTheme('name').

  • styles <object> required

    This is the object that defines your component's style props. The key names of this object become prop names in your component and the value handles the creation of style definitions. See creating style props below to learn the numerous ways you can generate styles.

  • props <object> required

    props allude to, as you'd guess, the props object provided to an element when it is created with React.createElement(). But it can be any plain object you want to derive styles from. useStyles() is an immutable function. That is, the input props are cloned and the return value is a new object containing a css prop with your generated styles. It will also remove any keys from the input props that generated styles. For example, let's say your component has a boolean style prop for hidden which provides a display: none; style:

    // Given the input props
    {hidden: true, foo: 'bar'}
    // Expect the output props
    foo: 'bar',
    css: [{
    name: "1ecy55y",
    styles: "display: none;"

2. Creating style props

There are three ways to create style props in a useStyles() hook

  1. Creating a boolean style prop

    This is the most basic type of style prop you can create. To use it, create a key in your styles object with an Emotion css object as its value.

    import {css} from '@emotion/core'
    // style definitions
    const styles = {
    // this creates a `boolean` prop for `hidden`
    hidden: css`display: none;`
    // ... add your hook MyComponent ... //
    <MyComponent hidden/>
    // Here, the prop `hidden` becomes
    // {css: [{name: '...', styles: 'display: none;'}]}
  2. Creating an enum style prop

    To construct an enum style prop just use the same pattern we used for a boolean prop, but as part of a nested object.

    import {css} from '@emotion/core'
    // style definitions
    const styles = {
    // this creates an `enum` prop for `display`
    // with the choices 'none', 'block', and 'flex'
    display: {
    none: css`display: none;`,
    block: css`display: block;`,
    flex: css`display: flex;`,
    // ... add your hook MyComponent ... //
    <MyComponent display='block'/>
    // Here, the prop `display` becomes
    // {css: [{name: '...', styles: 'display: block;'}]}
  3. Creating a functional style prop

    The true magic begins when you create functional style props. Here we can customize our styles based on the component theme and props passed to the useStyles() hook. Return an Emotion css object to add styles, or null/undefined to skip adding styles.

    Here is the function signature

    (value: <any>, theme: <object>, props: <object>): <css|void>

    • value <any>

      The value provided to the style prop in React.createElement().

    • theme <object>

      The theme as defined in ThemeProvider.

    • props <object>

      Other props that were passed to our useStyles() hook.

    import {css} from '@emotion/core'
    // fake default theme
    const theme = {
    box: {
    sizes: {
    sm: 16,
    md: 32,
    lg: 64
    // style definitions
    const styles = {
    // this creates a `functional` prop for `size`
    size: (value, theme, props) => {
    const px =[value]
    // in reality you'd throw an error, but for this example
    // we just skip the prop
    if (px === undefined)
    return css`
    width: ${px}px;
    height: ${px}px;
    // ... add your hook MyComponent ... //
    <MyComponent size='md'/>
    // Here, the prop `size` becomes
    // {css: [{name: '...', styles: 'width: 32px; height: 32px;'}]}

Another important thing to remember when constructing your style props is thatuseStyles will handle all of the breakpoint props on its own. You will never receive the breakpoint as part of your value. You will only receive the portion in front of the delimiter.

See here for a plethora of examples in the Curls <Box> component

3. Understanding the relationship between themes and hooks

Effectively, you can put anything you want to in your theme. The values in your theme are provided to your functional style props and accessible via useTheme(). There are, however, two special keys that Style Hooks uses in the useStyles() hook for a given hook name:

const theme = {
// assumes you've made a useText() hook w/ the
// name: 'text'
text: {
// default props prepended to the props object
// received by useStyles()
defaultProps: {
size: 'sm'
// groups of other default props applied when
// using kind='' props
kinds: {
h1: {
as: 'h1',
size: 'lg',
  • defaultProps <object>

    These style props are provided to every component of this type by default. As the name implies, they work just like React default props and can be overwritten by the user.

  • kinds <object>

    These are basically an extension to defaultProps, but in the form of variants. See the kind prop section.

4. Composing style hooks

Another cool feature of style hooks is that they are composable. For example, say you want all of your components to inherit style props from a useBox() hook providing access to margins, padding, and other props from the box model. You could do the following:

import {css, createStyleHook, createElement} from '@style-hooks/core'
// here is your hook
const useMyHook = createStyleHook('myComponent', {foo: css`bar: baz;`})
// here is a component composing multiple hooks
const MyComponent = React.forwardRef((props, ref) => {
// Here is the composition
props = useBox(useMyHook(props))
props.ref = ref
return createElement('div', props)
export {MyComponent, useMyComponent}
// Now you can use <MyComponent/> with the
// Box model in addition to your custom styles
<MyComponent foo d='block'/>
// {
// css: [
// {name: '...', styles: 'bar: baz;'},
// {name: '...', styles: 'display: block;'}
// ]
// }

5. Adding default styles to a hook

Sometimes you may want to add styles to a hook by default. Below is an example of how you may approach the task:

import {css} from '@emotion/core'
import {useStyles} from '@style-hooks/core'
const useRow = (props) => {
props = useStyles('row', {}, props)
props.css = [
display: flex;
flex-direction: row;
...props.css || []
return props

Next let's build a real style hook from start to finish

In this example, we are going to write our own useBox() hook

  1. Name your component so its configurable in the theme.

    import React from 'react'
    import {css} from '@emotion/core'
    import {useStyles} from '@style-hooks/core'
    const useBox = props => useStyles(
    // This is the name in our theme
  2. Next we add our style props. We only want our box to handle overflow, width, height, background color, and a display: block; boolean

    import React from 'react'
    import {css} from '@emotion/core'
    import {useStyles} from '@style-hooks/core'
    const useBox = props => useStyles(
    // This is the name in our theme
    // These are our style prop definitions
    // Adds a boolean prop
    block: css`display: block;`,
    // Adds an enum prop
    overflow: {
    hidden: css`overflow: hidden;`,
    hiddenX: css`overflow-x: hidden;`,
    hiddenY: css`overflow-y: hidden;`,
    auto: css`overflow: auto;`,
    autoY: css`overflow-y: auto;`,
    autoX: css`overflow-x: auto;`
    // Adds functional props
    bg: value =>
    css`background-color: ${value};`,
    width: (value, theme) =>
    css`width: ${value + theme.sizeUnit};`,
    height: (value, theme) =>
    css`height: ${value + theme.sizeUnit};`,
    // consumes the props received by your component
  3. Create a <Box> using our useBox() hook

    const Box = React.forwardRef((props, ref) => {
    // The style hook will generate a css prop and
    // remove prop keys that generated styles in
    // useBox()
    props = useBox(props)
    // props are mutable here because the props returned
    // by useBox() are always cloned from the input
    // props
    // using 'ref' here with React.forwardRef allows
    // our <Box> component to accept a 'ref' prop
    props.ref = ref
    // Here we're telling createElement we want the default element
    // type to be a 'div'. This can be overwritten using
    // an 'as' prop
    return createElement('div', props)
  4. Finally, since we referenced a sizeUnit in our functional style props for width and height above, let's put a size unit in our theme. While we're add it, we'll add some defaultProps and kind props.

    // Here's our theme where we specify a
    // size unit, default props, and kinds for
    // this box component
    const theme = {
    sizeUnit: 'px',
    box: {
    defaultProps: {
    bg: 'hotpink',
    width: 50,
    height: 50
    kinds: {
    small: {
    bg: 'blue',
    width: '100:phone 250:desktop',
    height: '100:phone 250:desktop'
    big: {
    bg: 'green',
    width: '300:phone 500:desktop',
    height: '300:phone 500:desktop'
  5. Now the fun part, let's use our useBox() hook and <Box> component

That's all I've got for this tutorial! Let me know what you think and feel free to ask me any questions to have on Twitter @jaredlunde.

Continue to Styled components
NPM icon
MIT License ©2019
Jared Lunde