Add status updates, system notes, bordered rows, and labeled dividers to a conversation. Pair with Message to build a thread.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"function GitBranchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M6 3v12" /> <circle cx="18" cy="6" r="3" /> <circle cx="6" cy="18" r="3" /> <path d="M18 9a9 9 0 0 1-9 9" /> </svg> )}function SearchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" /> </svg> )}export function Example() { return ( <div className="flex flex-col gap-3"> <Marker> <MarkerIcon> <GitBranchIcon /> </MarkerIcon> <MarkerContent>Switched to a new branch</MarkerContent> </Marker> <Marker> <MarkerContent className="shimmer">Thinking…</MarkerContent> </Marker> <Marker variant="separator"> <MarkerContent>Conversation compacted</MarkerContent> </Marker> <Marker> <MarkerIcon> <SearchIcon /> </MarkerIcon> <MarkerContent>Explored 4 files</MarkerContent> </Marker> </div> )}Installation
npx @arctis-sh/@arctis-sh/ui@latest add markerUsage
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"<Marker> <MarkerIcon> <CheckIcon /> </MarkerIcon> <MarkerContent>Explored 4 files</MarkerContent></Marker>Composition
Marker ├── MarkerIcon └── MarkerContent
Variants
Use variant to choose an inline, bordered, or separator layout.
import { Marker, MarkerContent } from "@/components/ui/marker"export function Example() { return ( <div className="flex flex-col gap-6"> <Marker> <MarkerContent>Inline note</MarkerContent> </Marker> <Marker variant="separator"> <MarkerContent>Section</MarkerContent> </Marker> <Marker variant="border"> <MarkerContent>Row boundary</MarkerContent> </Marker> </div> )}| Variant | Description |
| --- | --- |
| default | Inline marker for status, notes, and actions. |
| border | Default alignment with a bottom border under the row. |
| separator | Centered label with divider lines on each side. |
Status
For in-progress markers, set role="status" and pair with Spinner so assistive technology announces updates.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"import { Spinner } from "@/components/ui/spinner"export function Example() { return ( <div className="flex flex-col gap-3"> <Marker role="status"> <MarkerIcon> <Spinner aria-hidden="true" role="presentation" aria-label="" /> </MarkerIcon> <MarkerContent>Compacting conversation</MarkerContent> </Marker> <Marker role="status"> <MarkerIcon> <Spinner aria-hidden="true" role="presentation" aria-label="" /> </MarkerIcon> <MarkerContent>Running tests</MarkerContent> </Marker> </div> )}Shimmer
Add the shimmer class to MarkerContent for a streaming text effect. Pair it with a spinner or icon in MarkerIcon while work is in progress.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"import { Spinner } from "@/components/ui/spinner"function SearchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" /> </svg> )}export function Example() { return ( <div className="flex flex-col gap-3"> <Marker> <MarkerContent className="shimmer">Thinking…</MarkerContent> </Marker> <Marker role="status"> <MarkerIcon> <Spinner aria-hidden="true" role="presentation" aria-label="" /> </MarkerIcon> <MarkerContent className="shimmer">Thinking…</MarkerContent> </Marker> <Marker role="status"> <MarkerIcon> <SearchIcon /> </MarkerIcon> <MarkerContent className="shimmer">Reading 4 files</MarkerContent> </Marker> </div> )}Separator
Use variant="separator" for dates or section dividers in a thread.
import { Marker, MarkerContent } from "@/components/ui/marker"export function Example() { return ( <div className="flex flex-col gap-3"> <Marker variant="separator"> <MarkerContent>Today</MarkerContent> </Marker> <Marker variant="separator"> <MarkerContent>Worked for 42s</MarkerContent> </Marker> <Marker variant="separator"> <MarkerContent>Conversation compacted</MarkerContent> </Marker> </div> )}Border
Use variant="border" to keep the default alignment and separate the next row.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"function GitBranchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M6 3v12" /> <circle cx="18" cy="6" r="3" /> <circle cx="6" cy="18" r="3" /> <path d="M18 9a9 9 0 0 1-9 9" /> </svg> )}function SearchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" /> </svg> )}function FileTextIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /> <path d="M14 2v4a2 2 0 0 0 2 2h4" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /> </svg> )}export function Example() { return ( <div className="flex flex-col gap-3"> <Marker variant="border"> <MarkerIcon> <GitBranchIcon /> </MarkerIcon> <MarkerContent>Switched to release-candidate</MarkerContent> </Marker> <Marker variant="border"> <MarkerIcon> <SearchIcon /> </MarkerIcon> <MarkerContent>Reviewed 8 related files</MarkerContent> </Marker> <Marker variant="border"> <MarkerIcon> <FileTextIcon /> </MarkerIcon> <MarkerContent>Opened implementation notes</MarkerContent> </Marker> </div> )}With icon
MarkerIcon sits beside the content on one row.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"function GitBranchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M6 3v12" /> <circle cx="18" cy="6" r="3" /> <circle cx="6" cy="18" r="3" /> <path d="M18 9a9 9 0 0 1-9 9" /> </svg> )}function SearchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <circle cx="11" cy="11" r="8" /> <path d="m21 21-4.3-4.3" /> </svg> )}function CheckIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M5 13l4 4L19 7" /> </svg> )}export function Example() { return ( <div className="flex flex-col gap-3"> <Marker> <MarkerIcon> <GitBranchIcon /> </MarkerIcon> <MarkerContent>Switched to a new branch</MarkerContent> </Marker> <Marker> <MarkerIcon> <SearchIcon /> </MarkerIcon> <MarkerContent>Explored 4 files</MarkerContent> </Marker> <Marker> <MarkerIcon> <CheckIcon /> </MarkerIcon> <MarkerContent>Syncing completed</MarkerContent> </Marker> </div> )}Links and buttons
Pass render to turn the root into a real <a> or <button>.
"use client"import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"function GitBranchIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M6 3v12" /> <circle cx="18" cy="6" r="3" /> <circle cx="6" cy="18" r="3" /> <path d="M18 9a9 9 0 0 1-9 9" /> </svg> )}function RotateCcwIcon({ className }: { className?: string }) { return ( <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true" className={className} > <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" /> <path d="M3 3v5h5" /> </svg> )}export function Example() { return ( <div className="flex flex-col gap-3"> <Marker render={<a href="#" />}> <MarkerIcon> <GitBranchIcon /> </MarkerIcon> <MarkerContent>View the pull request</MarkerContent> </Marker> <Marker render={<button type="button" />} onClick={() => undefined}> <MarkerIcon> <RotateCcwIcon /> </MarkerIcon> <MarkerContent>Revert this change</MarkerContent> </Marker> </div> )}<Marker render={<a href="/pr/42" />}> <MarkerContent>View the pull request</MarkerContent></Marker>Accessibility
Markers are presentational by default. Choose semantics based on their purpose.
Status — for streaming or progress, set role="status" so assistive tech announces the update. Hide decorative spinners inside MarkerIcon.
Separators — labeled dividers need no role. Divider lines are decorative CSS. Do not put role="separator" on a labeled marker; that role ignores text content and takes its name from aria-label.
Icons — MarkerIcon is aria-hidden. For icon-only markers, add aria-label (or visible text).
Interactive — use render with a real link or button so the control is reachable and correctly named by its content.
API Reference
Marker
Root element. markerVariants is also exported for composing styles. Standard HTML attributes such as role, onClick, and aria-label pass through to the root or the element provided through render.
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "border" | "separator" | "default" |
| render | ReactElement | — |
| className | string | — |
<Marker> <MarkerContent>Explored 4 files</MarkerContent></Marker>MarkerIcon
Decorative icon slot. Always aria-hidden. Accepts native span props.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
<MarkerIcon> <CheckIcon /></MarkerIcon>MarkerContent
Text content for the marker. Accepts native span props. Use className="shimmer" for the streaming text effect.
| Prop | Type | Default |
|---|---|---|
| className | string | — |
<MarkerContent>Explored 4 files</MarkerContent>