Skip to content

Creating a Spacer Component


Created: Aug 28, 2023 – Last Updated: Aug 28, 2023

Tags: JavaScript

Digital Garden

I think the first time I really considered using a Spacer or Stack component was when I read Margin considered harmful (opens in a new tab) and later Let’s Bring Spacer GIFs Back! (opens in a new tab). If you think this is useful for you, read on.

I want to explain how to create a Spacer component in plain React, Chakra UI (opens in a new tab), and vanilla-extract (opens in a new tab). You can probably adapt the code to to your styling solution if you don’t use any of those three.

#Plain React

You can pass in any number here as there are no style tokens and type-safety on which numbers to use.

Plain React
import React from "react"

type SpacerProps = {
  axis: "vertical" | "horizontal"
  size: number
}

export const Spacer = ({ size, axis, ...rest }: SpacerProps) => {
  const width = axis === "vertical" ? "1px" : size
  const height = axis === "horizontal" ? "1px" : size

  return (
    <span
      style={{
        display: "block",
        width,
        minWidth: width,
        height,
        minHeight: height,
      }}
      {...rest}
    />
  )
}
Result

#Chakra UI

You’ll be using Chakra UI’s built-in scale for the spacing props (coming from width). So size will be a defined list of numbers or tokens like xl.

Chakra UI
import React from "react"
import { Box, BoxProps } from "@chakra-ui/react"

interface ISpacerProps extends BoxProps {
  size: BoxProps["width"]
  axis: "vertical" | "horizontal"
}

export const Spacer = ({ size, axis, ...rest }: ISpacerProps) => {
  const width = axis === "vertical" ? "1px" : size
  const height = axis === "horizontal" ? "1px" : size
  return (
    <Box
      as="span"
      width={width}
      height={height}
      minWidth={width}
      minHeight={height}
      display="block"
      {...rest}
    />
  )
}
Result

#vanilla-extract

For this example I didn’t want to install any third-party/additional packages for vanilla-extract. So it uses its CSS custom properties API.

spacer.tsx
ts
import React from "react"
import { type SpacerScale, spacerVariants } from "./spacer.css"
export const Spacer = ({
size,
axis,
...rest
}: {
size: SpacerScale
axis: "horizontal" | "vertical"
}) => <span data-axis={axis} className={spacerVariants[size]} {...rest} />
spacer.css.ts
ts
import { style, styleVariants, createVar } from "@vanilla-extract/css"
export type SpacerScale = "sm" | "md" | "lg"
const spacerScales = {
sm: "12px",
md: "16px",
lg: "24px",
}
const size = createVar()
const base = style({
display: "block",
vars: {
[size]: spacerScales.md,
},
})
export const spacerVariants = styleVariants(spacerScales, (scale) => [
base,
{
vars: {
[size]: scale,
},
selectors: {
"&[data-axis='vertical']": {
width: "1px",
minWidth: "1px",
height: size,
minHeight: size,
},
"&[data-axis='horizontal']": {
width: size,
minWidth: size,
height: "1px",
minHeight: "1px",
},
},
},
])

You could also use dessert-box (opens in a new tab) and create Atoms with Sprinkles (opens in a new tab). Then it would look like this:

ts
import * as React from "react"
import { Box } from "./box"
import { Atoms } from "./atoms.css"
interface ISpacerProps extends Atoms {
size: Atoms["width"]
axis: "vertical" | "horizontal"
}
export const Spacer = ({ size, axis, ...rest }: ISpacerProps) => {
const width = axis === `vertical` ? `px` : size
const height = axis === `horizontal` ? `px` : size
return (
<Box
as="span"
width={width}
height={height}
minWidth={width}
minHeight={height}
display="block"
{...rest}
/>
)
}

Want to learn more? Browse my Digital Garden