Creating a Spacer Component
I think the first time I really considered using a Spacer
or Stack
component was when I read Margin considered harmful and later Let’s Bring Spacer GIFs Back!. 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, and vanilla-extract. 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.
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} /> ) }
#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
.
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} /> ) }
#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.
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} />
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
and create Atoms with Sprinkles. Then it would look like this:
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} /> )}