Separator
A line that divides content, and — unless you opt out — tells a screen reader
that it does. It runs either way: horizontal across stacked content,
vertical down a row. Give it children and it splits around them, a label
set into the line.
Billing
Visa ending 4242 · renews 1 September
Notifications
Email only · digest at 09:00
Import
import { Separator } from "@forte-ui/react";Examples
Vertical
Set orientation="vertical" and the rule turns, along with the
aria-orientation it reports.
A vertical separator is an empty box, so it has no length of its own. Inside a
flex row it stretches to the tallest sibling; anywhere else — in ordinary flow,
or between inline items like the metadata below — it falls back to one line's
height. That floor is --forte-separator-min-length, and it is the only reason a
vertical rule is ever visible outside a flex container.
In a toolbar the stretch is the point: the rule matches whatever it sits between, without anyone measuring the buttons.
Line style
variant is the whole visual API — a rule has exactly one dimension. solid
reads as a structural division; dashed and dotted read as provisional ones,
which is what you want for a drop zone's edge or an optional section.
With a label
Put children on the rule and the line splits around them — one half either
side, the label between. or between two ways in is the canonical case:
It works vertically too. The label stays upright, with a half-line above and below it, and in a flex row the halves stretch to the taller sibling exactly as the plain rule does:
Upload a file
CSV or JSON, up to 10 MB.
Paste a link
One difference from the plain rule: a labeled vertical one has no length floor. Outside a flex or grid row its halves have nothing to be, and only the label shows. That is deliberate — the floor the halves would need is two line boxes plus the label, and a flex row is sized from its items, so a toolbar-height row holding one would grow to fit it. Give it a row.
Text is the usual label, but anything renders — an icon, a Badge, a Kbd.
The label is deliberately no louder than the rule it sits on: small, muted
and medium weight, from --forte-separator-label-font-size,
--forte-separator-label-color and --forte-separator-label-font-weight,
with --forte-separator-label-gap between it and the line. A label on a
divider is secondary text by definition; if yours needs to be a heading, it
is a heading, and the rule should sit under it instead.
A labeled rule is named by its label — see Accessibility.
Spacing and inset
Two knobs place the rule, and they are named after the axes rather than after
margin because which physical side each one touches flips with the
orientation:
--forte-separator-spacingruns along the axis being separated — the gap between the rule and the content on either side of it.--forte-separator-insetruns across it — how far the rule is pulled in from the container's edges. Negative values push it back out, which is how a rule inside a padded panel becomes full-bleed.
Both default to 0, because the common case is a flex or grid parent whose
gap already does the spacing and a built-in margin would double it.
In prose
Separator renders a <div>. Where the division is a genuine thematic break
in running text, swap the element for the one HTML already has:
<Separator render={<hr />} />The styles follow. The UA gives <hr> an inset 3D border and a 0.5em auto
margin; both are cleared before this component paints its own single edge, so
an <hr> and a <div> separator are pixel-identical.
Not with a label, though: <hr> is a void element and cannot hold one.
Accessibility
Base UI renders role="separator" with an aria-orientation matching the
prop, so the division is announced rather than merely drawn.
That is right when the rule is the grouping. It is noise when the grouping is
already carried by a heading, a landmark or a list and the rule only reinforces
it visually — a screen reader then reads the page's decoration out loud. Pass
decorative for that case:
<Separator decorative />Labels
A labeled rule is named by its label: the root carries aria-labelledby
pointing at it, so a screen reader announces "or, separator" rather than
passing over the line. The attribute is doing real work: separator is a
role whose children are presentational, so text left loose inside one is not
guaranteed to be exposed at all. Your own aria-label or aria-labelledby
wins if you pass one.
decorative still drops the role, and only the role. There is no name to
give a role="none" element, so the label simply stays in the tree as plain
text — right for a date between two runs of messages, which a screen reader
should still hear. Where the label is visual rhetoric and nothing more — or
between two buttons that already say what they do — hide the whole thing:
<Separator decorative aria-hidden>or</Separator>Theming
The line is painted with a border, never background-color. Under
forced-colors a background is replaced with a system color — for a plain box
that is Canvas, the page's own backdrop — so a hairline drawn that way
disappears for exactly the users who most need the grouping cue. Border colors
are forced to CanvasText instead, so the rule survives with no forced-colors
rule in the stylesheet at all.
Nothing here transitions. A separator has no states, so there is nothing to animate, and a color transition would only make it lag behind the rest of the page on a theme switch.
| Property | Controls | Default |
|---|---|---|
--forte-separator-color | Line color | var(--forte-color-border-muted) |
--forte-separator-thickness | Line thickness | 1px |
--forte-separator-style | Line style; variant sets this | solid |
--forte-separator-spacing | Margin along the axis being separated | 0px |
--forte-separator-inset | Margin across it — how far in from the edges | 0px |
--forte-separator-min-length | Length floor for a vertical rule with nothing to stretch against | 1lh |
--forte-separator-label-gap | Space between the label and the half-line either side of it | var(--forte-space-3) |
--forte-separator-label-color | Label color | var(--forte-color-foreground-muted) |
--forte-separator-label-font-size | Label font size | var(--forte-font-size-1) |
--forte-separator-label-font-weight | Label font weight | var(--forte-font-weight-medium) |
State is on data-orientation and data-variant, with data-labeled beside
them when there is a label, so a consumer can reach all three from plain CSS
or a Tailwind arbitrary variant (data-[orientation=vertical]:...) without
wrapping the component. The label itself is data-forte="separator-label".
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | A label set into the line — `or` between two ways to sign in, a date between two runs of messages. The rule splits around it, one half either side, in both orientations. Text is the usual case, but anything renders: an icon, a `Badge`, a `Kbd`. A labeled rule is also *named* by its label. The root gets `aria-labelledby` pointing at it, so a screen reader announces the label as the separator's name instead of passing over it — `separator` is a role whose children are presentational, so text left loose inside one is not guaranteed to be exposed at all. Pass an `aria-label` or `aria-labelledby` of your own to name it differently. Under `decorative` there is no name to give: the role goes, and the label stays in the tree as plain text. Not with `render={<hr />}` — `<hr>` is a void element and cannot hold one. | |
className | string | Additional class name(s). Applied after the internal styles so consumer utilities (e.g. Tailwind) win without needing `!important`. | |
decorative | boolean | false | Drops `role="separator"`, leaving the line visible but absent from the accessibility tree. Use it when the grouping is already conveyed some other way — a heading, a landmark, a list — and the rule is only reinforcing it visually. Announcing a boundary that the structure already communicates makes a screen reader read the page's decoration out loud. |
orientation | SeparatorOrientation | horizontal | Which way the rule runs — and therefore which axis it separates. A `horizontal` rule is a line across the inline axis dividing stacked content; a `vertical` one is a line down the block axis dividing content in a row. It is also what Base UI reports as `aria-orientation`, so it is a semantic choice, not only a visual one. |
variant | SeparatorVariant | solid | The line style. A rule has exactly one visual dimension, so this is the whole of it: `solid` for structural divisions, `dashed` or `dotted` for softer, more provisional ones (a drop zone's edge, an optional section). |
Everything else passes through to Base UI's Separator.