Skip to content
Primitiv home
Framework
Consumption mode

Divider

stableSource Figma

A separator between content sections — a <span role="separator"> whose aria-orientation drives a horizontal or vertical rule.

Playground

Density

Preview

Section oneSection two
Orientation
import { Divider } from "@/components/ui/divider";
<Divider orientation="horizontal" />

Density is set by a data-density ancestor — the Context system, not a Divider prop.

Installation

npx primitiv add divider

Import

import { Divider } from "@/components/ui/divider";

Copied into your project as .primitiv-divider — you own the file afterwards, so upgrades are opt-in.

Headless mode installs the npm package instead: @primitiv-ui/react

Props

Divider

Extends HTMLSpanElement — every native attribute of that element is accepted and forwarded.

PropTypeDefaultFromDescription
orientation"horizontal" | "vertical""horizontal"headlessAxis the separator runs along. Sets aria-orientation on the rendered <span role="separator"> so screen readers announce the correct orientation. Use "horizontal" for row-dividing rules and "vertical" for column-dividing rules.

Styling contract

--primitiv-divider-color--primitiv-divider-thickness--primitiv-divider-spacing

Accessibility

  • The rule renders as <span role="separator">, so assistive tech announces it as a boundary between content groups rather than as decoration.
  • orientation sets aria-orientation on that element — "horizontal" (the default) or "vertical" — so the announced axis matches what is drawn.
  • There is no keyboard interaction: a separator is not focusable and takes no input, which is correct — it marks a boundary, it does not act on one.
  • For a purely decorative rule, pass aria-hidden="true". A separator whose meaning is already carried by the surrounding structure is noise in the accessibility tree, and hiding it is the right call there.

Examples

Separating sections

The default: a full-width hairline that spans its container and reads as a real role="separator" to assistive tech. Note the breathing room around it is the container's gap, not the rule — Divider reserves no separation of its own (see below).

Density
Section oneSection two
import { Divider } from "@/components/ui/divider";import { Stack } from "@/components/ui/stack";
<Stack gap="lg">  <section>{/* ... */}</section>  <Divider />  <section>{/* ... */}</section></Stack>

A vertical rule

orientation="vertical" sets aria-orientation and turns the rule 90°. A vertical divider takes its height from the flex row it sits in — a flex item stretches to the row's cross-axis by default — so a plain block or column gives it no height to span. Ideal between inline metadata or toolbar groups.

Density
OverviewPricingDocs
import { Divider } from "@/components/ui/divider";import { Stack } from "@/components/ui/stack";
<Stack direction="row" gap="md">  <span>Overview</span>  <Divider orientation="vertical" />  <span>Pricing</span>  <Divider orientation="vertical" />  <span>Docs</span></Stack>

Spacing belongs to the container

Divider ships no margin by default — separation is the container's job. A gap-based container already spaces its children, and a margin baked into the rule would double that, could never reach 0, and could not track data-density. When a plain block-flow context has no gap of its own, give the rule its own margin: the styled surface exposes --primitiv-divider-spacing for exactly this, and in headless — where no styles ship at all — you set the margin directly.

Density

Above the rule.

Below the rule.

import { Divider } from "@/components/ui/divider";
{/* Plain block flow — no container gap, so the rule reserves its own. */}<article>  <p>{/* ... */}</p>  <Divider style={{ "--primitiv-divider-spacing": "var(--primitiv-space-space-16)" }} />  <p>{/* ... */}</p></article>

Decorative vs semantic

Left to itself, a Divider announces as a separator. When the rule is purely visual — the surrounding structure already groups the content — pass aria-hidden="true" to drop it from the accessibility tree so a screen reader is not told about a line that carries no meaning. Keep the semantic form when the divider genuinely separates distinct groups, like sections of a menu.

Density
Semantic separatorDecorative ruleEnd
import { Divider } from "@/components/ui/divider";
{/* Semantic: a real boundary between content groups. */}<Divider />
{/* Decorative: purely visual, hidden from assistive tech. */}<Divider aria-hidden="true" />