How to Insert Separator Element Between Flexbox Items in React

June 22, 2023

2 min read

How to Insert Separator Element Between Flexbox Items in React
Watch on YouTube

It's quite a common UI pattern to add a delimiter between elements, such as a dot or a slash, to separate pieces of text or create divisions between sections on a page. Today, I would like to share abstract React components that are built on top of basic flexbox CSS, allowing you to implement such use cases in seconds.

Demo
Demo

Let's start with the StackSeparatedBy component and its horizontal version, HStackSeparatedBy. These components take a separator element and render it between their children. They are built on top of the Stack component, which I have described in detail in this post. By using Children.toArray, we can remove null children. However, it's important to note that if one of the children is a component that returns null, we won't be able to recognize it.

import React, { Fragment, ReactNode } from "react"
import { Stack, StackProps } from "./Stack"
import { isLast } from "lib/shared/utils/isLast"

export const dotSeparator = "•"
export const slashSeparator = "/"

export interface StackSeparatedByProps extends StackProps {
  separator: ReactNode
}

export const StackSeparatedBy = ({
  children,
  separator,
  gap = 8,
  wrap = "wrap",
  ...rest
}: StackSeparatedByProps) => {
  const items = React.Children.toArray(children)
  return (
    <Stack wrap={wrap} gap={gap} {...rest}>
      {items.map((child, index) => {
        if (isLast(items, index)) {
          return child
        }
        return (
          <Fragment key={index}>
            {child}
            {separator}
          </Fragment>
        )
      })}
    </Stack>
  )
}

export interface HStackSeparatedByProps
  extends Omit<StackSeparatedByProps, "direction"> {}

export const HStackSeparatedBy = ({
  alignItems = "center",
  ...props
}: HStackSeparatedByProps) => {
  return <StackSeparatedBy direction="row" alignItems={alignItems} {...props} />
}

To add lines between sections, we have the SeparatedByLine component. While it's also built on top of the Stack component, it uses CSS to add a border-bottom and padding-bottom to all children except the last one. Here, we don't want to iterate over elements because sections are usually more complex components, and they might return null. In such cases, we won't be able to recognize it because children would still be a component.

import styled, { css } from "styled-components"

import { VStack } from "./Stack"
import { getCSSUnit } from "./utils/getCSSUnit"
import { getColor } from "./theme/getters"

export const SeparatedByLine = styled(VStack)`
  > *:not(:last-child) {
    border-bottom: 1px solid ${getColor("backgroundGlass2")};
    padding-bottom: ${({ gap = 0 }) => getCSSUnit(gap)};
  }
`