A divider that labels a moment inside a conversation or feed: a centered label between two lines. Use date for a new day, unread for where new messages start, and event for something that happened, like someone joining.
Bring your own model.
These components only show what you give them, in one message shape, and report what the person does. They never call a model or a server, so they work with any backend. They are built for how assistive technology handles streaming text, tool steps and approvals.
import { Bubble, BubbleList, Marker } from "@rdloom/react";
export default function MarkerDatesExample() {
return (
<div className="flex w-full justify-center">
<BubbleList className="w-full max-w-md">
<Marker label="Yesterday" dateTime="2026-10-07" />
<Bubble from="user">See you tomorrow.</Bubble>
<Marker label="Today" dateTime="2026-10-08" />
<Bubble>Morning! Ready when you are.</Bubble>
</BubbleList>
</div>
);
}Installation
npx rdloom add markerCopies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.
Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/marker.jsonWorks, but without upgrade tracking.
Usage
import { Bubble, BubbleList, Marker } from "@rdloom/react";
<div className="flex w-full justify-center">
<BubbleList className="w-full max-w-md">
<Marker label="Yesterday" dateTime="2026-10-07" />
<Bubble from="user">See you tomorrow.</Bubble>
<Marker label="Today" dateTime="2026-10-08" />
<Bubble>Morning! Ready when you are.</Bubble>
</BubbleList>
</div>Unread
import { useState } from "react";
import { Bubble, BubbleList, Marker } from "@rdloom/react";
export default function MarkerUnreadExample() {
const [jumped, setJumped] = useState(false);
return (
<div className="flex w-full justify-center">
<BubbleList className="w-full max-w-md">
<Bubble>Thanks for the update.</Bubble>
<Marker variant="unread" label="New messages" jumpLabel="Jump to first new message" onJump={() => setJumped(true)} />
<Bubble>Can you review the draft?</Bubble>
{jumped && (
<p role="status" className="text-center text-xs text-[var(--rd-color-text-muted)]">
Jumped to the first new message
</p>
)}
</BubbleList>
</div>
);
}Events
import { Marker } from "@rdloom/react";
export default function MarkerEventsExample() {
return (
<div className="flex w-full justify-center">
<div className="flex w-full max-w-md flex-col gap-2">
<Marker variant="event" label="Alex joined" />
<Marker variant="event" label="Topic changed to Launch plan" />
<Marker variant="event" label="Sam left" />
</div>
</div>
);
}API Reference
Defined by the spec. Components also accept the props of the React Aria component they wrap.
| Prop | Type | Default |
|---|---|---|
labelrequiredThe text, e.g. "Today", "New messages" or "Alex joined". | string | none |
variantdate is neutral. unread uses the action colour for the label and lines. event is a smaller, muted note. | "date" | "unread" | "event" | "date" |
dateTimeA machine readable date for a date marker, e.g. "2026-10-08". The label becomes a time element. | string | none |
onJumpTurns the label of an unread marker into a button, e.g. to scroll to the first new message. | () => void | none |
jumpLabelAccessible name of the jump button when it needs more than the label, e.g. "Jump to first new message". | string | none |
Accessibility
Role separator, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.
Keyboard
- Tab: moves to the jump button when onJump is given
- Enter or Space: runs onJump
Screen readers announce
- "Today, separator"
- "New messages, button" when it can be jumped to
What your code must do
- Without onJump the marker is a separator named by its label, so the change of moment is announced once
- With onJump the marker is a labelled group holding a real button, because a separator cannot contain controls
- The lines are decorative and hidden from assistive technology
- The unread variant also says its meaning in words ("New messages"), not only in colour
- The label meets 4.5:1 on the background
Guidelines
Use it when
- Starting a new day in a long conversation
- Showing where unread messages begin
- Noting an event such as a person joining or a topic change
Avoid it when
- A plain line between unrelated content: use Separator
- A section title that structures a page: use a heading
Don't
- A marker for every message
- A long sentence as the label: keep it to a few words
- Using colour alone to mean unread
Design tokens
The semantic tokens this component uses. Change them once and every component follows; see Design tokens.
--rd-color-border-default--rd-color-text-muted--rd-color-action-primary--rd-color-focus-ring--rd-radius-control