Separator
A simple horizontal or vertical divider line used to visually separate sections of content or groups of related elements.
Search across all documentation pages
A simple horizontal or vertical divider line used to visually separate sections of content or groups of related elements.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
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>.
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.
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.
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.
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.
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.
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.
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:
orientation prop.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.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.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.Separator and inner SeparatorLine accept refs, allowing animation libraries or measurement logic to attach directly to the DOM element.my-) and vertical separators use horizontal margin (mx-), so spacing always applies in the correct axis.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.
Reviewed by Chris St. John·Last updated Jul 7, 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥