React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

separatordividerlinehrdelimitercomponenttailwind

Separator

A simple horizontal or vertical divider line used to visually separate sections of content or groups of related elements.

Use Cases

  • Separate sections of a settings page or form
  • Divide navigation groups in a sidebar menu
  • Break apart card content into logical blocks
  • Create visual distinction between list items
  • Split a layout into left and right panels with a vertical divider
  • Provide an "or" divider between alternative actions (e.g., social login vs email)
  • Separate header, body, and footer areas within a modal or card

Simplest Implementation

export function Separator() {
  return <hr className="border-t border-gray-200" />;
}

A plain horizontal rule styled with Tailwind. No "use client" is needed since there is no interactivity. The border-t ensures only the top border renders, avoiding the double-line effect of a default <hr>.

Variations

Horizontal Separator

interface SeparatorProps {
  className?: string;
}
 
export function Separator({ className }: SeparatorProps) {
  return (
    <hr
      role="separator"
      aria-orientation="horizontal"
      className={`border-t border-gray-200 ${className ?? ""}`}
    />
  );
}

Adds explicit ARIA attributes so assistive technologies announce the divider correctly. The className prop allows consumers to override spacing or color on a per-use basis.

Vertical Separator

interface SeparatorProps {
  className?: string;
}
 
export function VerticalSeparator({ className }: SeparatorProps) {
  return (
    <div
      role="separator"
      aria-orientation="vertical"
      className={`h-full w-px bg-gray-200 ${className ?? ""}`}
    />
  );
}
 
// Usage inside a flex row
function Toolbar() {
  return (
    <div className="flex h-10 items-center gap-4">
      <button className="text-sm">Cut</button>
      <VerticalSeparator />
      <button className="text-sm">Copy</button>
      <VerticalSeparator />
      <button className="text-sm">Paste</button>
    </div>
  );
}

A vertical divider rendered as a div with w-px width and h-full to stretch to the parent height. It must live inside a flex container with a defined height, otherwise the separator collapses to zero.

With Text Label

interface LabeledSeparatorProps {
  label: string;
}
 
export function LabeledSeparator({ label }: LabeledSeparatorProps) {
  return (
    <div className="flex items-center gap-4">
      <div className="h-px flex-1 bg-gray-200" />
      <span className="text-sm text-gray-500">{label}</span>
      <div className="h-px flex-1 bg-gray-200" />
    </div>
  );
}
 
// Usage
<LabeledSeparator label="or" />

The classic "or" divider pattern. Two flex-1 lines expand equally on each side of the centered text. Using h-px instead of border-t gives more predictable alignment when flex items have varied heights.

Dashed Style

interface SeparatorProps {
  className?: string;
}
 
export function DashedSeparator({ className }: SeparatorProps) {
  return (
    <hr
      role="separator"
      className={`border-t border-dashed border-gray-300 ${className ?? ""}`}
    />
  );
}

The border-dashed utility creates a dotted divider that feels lighter than a solid line. Useful in form sections or settings panels where a subtle separation is needed without hard visual boundaries.

With Spacing Variants

type SpacingSize = "none" | "sm" | "md" | "lg" | "xl";
 
interface SeparatorProps {
  spacing?: SpacingSize;
  className?: string;
}
 
const spacingClasses: Record<SpacingSize, string> = {
  none: "my-0",
  sm: "my-2",
  md: "my-4",
  lg: "my-6",
  xl: "my-8",
};
 
export function Separator({ spacing = "md", className }: SeparatorProps) {
  return (
    <hr
      role="separator"
      className={`border-t border-gray-200 ${spacingClasses[spacing]} ${className ?? ""}`}
    />
  );
}

Predefined spacing presets prevent ad-hoc margin values from appearing throughout the codebase. The none option is useful when the parent container already manages spacing via gap or padding.

Decorative Gradient

interface SeparatorProps {
  className?: string;
}
 
export function GradientSeparator({ className }: SeparatorProps) {
  return (
    <div
      role="separator"
      aria-orientation="horizontal"
      className={`h-px bg-gradient-to-r from-transparent via-gray-300 to-transparent ${className ?? ""}`}
    />
  );
}

A gradient line that fades in from both edges, creating a softer visual separation. Uses Tailwind's via- modifier for a three-stop gradient. This works best in hero sections or between large content blocks where a traditional rule feels too heavy.

Complex Implementation

import { forwardRef } from "react";
 
type Orientation = "horizontal" | "vertical";
type Variant = "solid" | "dashed" | "dotted" | "gradient";
type Thickness = "thin" | "medium" | "thick";
type SpacingSize = "none" | "sm" | "md" | "lg" | "xl";
 
interface SeparatorProps {
  orientation?: Orientation;
  variant?: Variant;
  thickness?: Thickness;
  spacing?: SpacingSize;
  label?: string;
  color?: string;
  className?: string;
}
 
const spacingMap: Record<Orientation, Record<SpacingSize, string>> = {
  horizontal: { none: "", sm: "my-2", md: "my-4", lg: "my-6", xl: "my-8" },
  vertical: { none: "", sm: "mx-2", md: "mx-4", lg: "mx-6", xl: "mx-8" },
};
 
const thicknessMap: Record<Orientation, Record<Thickness, string>> = {
  horizontal: { thin: "h-px", medium: "h-0.5", thick: "h-1" },
  vertical: { thin: "w-px", medium: "w-0.5", thick: "w-1" },
};
 
export const Separator = forwardRef<HTMLDivElement, SeparatorProps>(
  function Separator(
    {
      orientation = "horizontal",
      variant = "solid",
      thickness = "thin",
      spacing = "md",
      label,
      color = "gray-200",
      className,
    },
    ref
  ) {
    const spacingClass = spacingMap[orientation][spacing];
 
    if (label && orientation === "horizontal") {
      return (
        <div
          ref={ref}
          role="separator"
          aria-orientation="horizontal"
          className={`flex items-center gap-4 ${spacingClass} ${className ?? ""}`}
        >
          <SeparatorLine
            orientation="horizontal"
            variant={variant}
            thickness={thickness}
            color={color}
          />
          <span className="shrink-0 text-sm text-gray-500">{label}</span>
          <SeparatorLine
            orientation="horizontal"
            variant={variant}
            thickness={thickness}
            color={color}
          />
        </div>
      );
    }
 
    return (
      <SeparatorLine
        ref={ref}
        orientation={orientation}
        variant={variant}
        thickness={thickness}
        color={color}
        className={`${spacingClass} ${className ?? ""}`}
      />
    );
  }
);
 
interface LineProps {
  orientation: Orientation;
  variant: Variant;
  thickness: Thickness;
  color: string;
  className?: string;
}
 
const SeparatorLine = forwardRef<HTMLDivElement, LineProps>(
  function SeparatorLine({ orientation, variant, thickness, color, className }, ref) {
    const size = thicknessMap[orientation][thickness];
    const stretch = orientation === "horizontal" ? "w-full" : "h-full";
 
    if (variant === "gradient") {
      const direction = orientation === "horizontal" ? "bg-gradient-to-r" : "bg-gradient-to-b";
      return (
        <div
          ref={ref}
          role="separator"
          aria-orientation={orientation}
          className={`${size} ${stretch} ${direction} from-transparent via-${color} to-transparent ${className ?? ""}`}
        />
      );
    }
 
    const borderSide = orientation === "horizontal" ? "border-t" : "border-l";
    const borderStyle =
      variant === "dashed" ? "border-dashed" : variant === "dotted" ? "border-dotted" : "";
    const borderThickness =
      thickness === "thick" ? `${borderSide}-4` : thickness === "medium" ? `${borderSide}-2` : borderSide;
 
    return (
      <div
        ref={ref}
        role="separator"
        aria-orientation={orientation}
        className={`${stretch} ${borderThickness} border-${color} ${borderStyle} ${className ?? ""}`}
      />
    );
  }
);

Key aspects:

  • Dual orientation -- a single component handles both horizontal and vertical dividers by swapping dimension and border-direction classes based on the orientation prop.
  • Label support -- when a label is provided on a horizontal separator, the component splits into two lines flanking the centered text, using the same SeparatorLine sub-component for consistency.
  • Variant system -- solid, dashed, dotted, and gradient cover the most common visual styles. The gradient variant uses a completely different rendering path (background gradient vs border) to avoid conflicting CSS properties.
  • Proportional thickness -- border-based variants map thickness to border-t, border-t-2, and border-t-4, while gradient variants use h-px, h-0.5, and h-1 for equivalent visual weight.
  • forwardRef -- both the outer Separator and inner SeparatorLine accept refs, allowing animation libraries or measurement logic to attach directly to the DOM element.
  • Spacing maps per orientation -- horizontal separators use vertical margin (my-) and vertical separators use horizontal margin (mx-), so spacing always applies in the correct axis.

Gotchas

  • Vertical separator collapses to zero height -- a vertical separator with h-full requires a parent with an explicit height or a flex container. Without it, the divider is invisible.

  • Tailwind purge strips dynamic color classes -- interpolating border-${color} or via-${color} into class strings causes Tailwind to purge those classes in production. Use a safelist or a static class map instead.

  • <hr> has default margin -- browsers apply a default margin to <hr> elements. If you do not reset it with my-0 or your own margin class, you get unexpected extra spacing.

  • ARIA role on decorative dividers -- purely decorative separators should use role="none" or aria-hidden="true" instead of role="separator" to avoid cluttering the accessibility tree with meaningless landmarks.

  • Gradient direction ignored in dark mode -- if using via-gray-300 for the gradient midpoint, it may become invisible on dark backgrounds. Swap to via-gray-600 in dark mode using Tailwind's dark: prefix.

  • Border-dashed rendering varies by browser -- the length and gap of dashes differ between Chrome, Firefox, and Safari. If pixel-perfect dashes are required, use an SVG pattern or a repeating background image instead.

  • Card -- Separators are often used inside cards to divide header, body, and footer
  • Sidebar -- Vertical and horizontal separators divide nav groups in sidebars
  • Modal -- Modals use separators between title, content, and action areas
  • Tabs -- A separator below the tab bar distinguishes tabs from panel content