DEV Community

Cover image for How to Insert Separator Element Between Flexbox Items in React
Radzion Chachura
Radzion Chachura

Posted on • Edited on • Originally published at radzion.com

How to Insert Separator Element Between Flexbox Items in React

Watch on YouTube | 🐙 GitHub | 🎮 Demo

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

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} />
}
Enter fullscreen mode Exit fullscreen mode

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)};
  }
`
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
aarone4 profile image
Aaron Reese

The link to the base component is broken :(
And it might be worth including a link to the utils.isLast too.
Without being able to view the base code, I am interested in why the horizontal version is a wrapper around the vertical component rather than just a different extension of the base component; particularly how the Omits<> construct is used to override the base direction as this is now two abstract levels away. Just from a clean code perspective would it make more sense to omit direction from the StackSeparatedByProps and then include it explicitly in both derived components

Collapse
 
radzion profile image
Radzion Chachura

@aarone4 thanks for pointing out the broken link, here's the blog-post about the stack: radzion.com/blog/stacks

and source code: github.com/RodionChachura/reactkit...