Creating a Spacer Component
Created: – Last Updated:
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.
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 (opens in a new tab) and create Atoms with Sprinkles (opens in a new tab). 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} /> )}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} /> ) }
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} /> )}import React from "react"import { Spacer } from "./spacer"
export default function App() { return ( <main> <div>Spacer</div> {/* Increase the size value */} <Spacer axis="vertical" size={16} /> <div>Some text below</div> </main> )}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} /> ) }
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} /> )}import React from "react"import { ChakraProvider } from "@chakra-ui/react"import { Spacer } from "./spacer"
export default function App() { return ( <ChakraProvider> <main> <div>Spacer</div> {/* Use one of the tokens */} <Spacer axis="vertical" size="16" /> <div>Some text below</div> </main> </ChakraProvider> )}