import { GitBranchIcon, SearchIcon } from "lucide-react"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
import { Spinner } from "@/components/ui/spinner"
export function MarkerDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<Marker>
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Switched to a new branch</MarkerContent>
</Marker>
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<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 shadcn@latest add @fulldev/marker
Usage
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
<Marker>
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
<MarkerContent>Explored 4 files</MarkerContent>
</Marker>
Composition
Use the following composition to build a marker:
Marker
├── MarkerIcon
└── MarkerContent
Variants
Use variant to switch between an inline marker, bordered row, and labeled separator.
import { Marker, MarkerContent } from "@/components/ui/marker"
export function MarkerVariantsDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<Marker>
<MarkerContent>A default marker for inline notes.</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerContent>A separator marker</MarkerContent>
</Marker>
<Marker variant="border">
<MarkerContent>A border marker for row boundaries.</MarkerContent>
</Marker>
</div>
)
}| Variant | Description |
|---|---|
default | An inline marker for status, notes, and actions. |
border | A default marker with a bottom border under the row. |
separator | A centered label with divider lines on each side. |
Status
Set role="status" and include a Spinner for streaming or in-progress markers so updates are announced.
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
import { Spinner } from "@/components/ui/spinner"
export function MarkerStatusDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Compacting conversation</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Running tests</MarkerContent>
</Marker>
</div>
)
}Shimmer
Add the shimmer utility class to MarkerContent for an animated streaming-text effect.
import { Marker, MarkerContent } from "@/components/ui/marker"
export function MarkerShimmerDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<Marker role="status">
<MarkerContent className="shimmer">Thinking...</MarkerContent>
</Marker>
<Marker variant="separator" role="status">
<MarkerContent className="shimmer">Reading 4 files</MarkerContent>
</Marker>
</div>
)
}Separator
Use the separator variant for labeled dividers, such as dates or section breaks, in a conversation.
import { Marker, MarkerContent } from "@/components/ui/marker"
export function MarkerSeparatorDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<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 the border variant for status rows that should keep the default marker alignment while separating the next row.
import { FileTextIcon, GitBranchIcon, SearchIcon } from "lucide-react"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
export function MarkerBorderDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-3 py-12">
<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
Use MarkerIcon to render an icon alongside the content.
import { BookOpenCheck, GitBranchIcon, SearchIcon } from "lucide-react"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
export function MarkerIconDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-12 py-12">
<Marker>
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>Switched to a new branch</MarkerContent>
</Marker>
<Marker variant="separator">
<MarkerIcon>
<SearchIcon />
</MarkerIcon>
<MarkerContent>Explored 4 files</MarkerContent>
</Marker>
<Marker className="flex-col">
<MarkerIcon>
<BookOpenCheck />
</MarkerIcon>
<MarkerContent>Syncing completed</MarkerContent>
</Marker>
</div>
)
}Links and Buttons
Turn a marker into a link or button with the render prop on Marker.
"use client"
import { GitBranchIcon, RotateCcwIcon } from "lucide-react"
import { toast } from "sonner"
import { Marker, MarkerContent, MarkerIcon } from "@/components/ui/marker"
export function MarkerLinkButtonDemo() {
return (
<div className="flex w-full max-w-sm flex-col gap-8 py-12">
<Marker render={<a href="#links-and-buttons" />}>
<MarkerIcon>
<GitBranchIcon />
</MarkerIcon>
<MarkerContent>View the pull request</MarkerContent>
</Marker>
<Marker
render={
<button
type="button"
className="transition-colors hover:text-foreground"
onClick={() => toast("You clicked the revert button")}
/>
}
>
<MarkerIcon>
<RotateCcwIcon />
</MarkerIcon>
<MarkerContent>Revert this change</MarkerContent>
</Marker>
</div>
)
}import { Marker, MarkerContent } from "@/components/ui/marker"
export function MarkerLinkDemo() {
return (
<Marker render={<a href="#" />}>
<MarkerContent>View the pull request</MarkerContent>
</Marker>
)
}
Accessibility
Marker is presentational by default.
Status and Progress
For streaming or progress markers such as “Thinking…” or a running tool, set role="status" so assistive tech announces the update as it appears.
<Marker role="status">
<MarkerIcon>
<Spinner />
</MarkerIcon>
<MarkerContent>Compacting conversation</MarkerContent>
</Marker>
Labeled Separators
A separator that carries text, such as a date or a section label, needs no role.
<Marker variant="separator">
<MarkerContent>Today</MarkerContent>
</Marker>
Bordered Markers
A bordered marker keeps the same semantics as the default marker.
<Marker variant="border">
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Opened implementation notes</MarkerContent>
</Marker>
Decorative Icons
MarkerIcon is decorative and hidden from assistive tech with aria-hidden, so the adjacent MarkerContent carries the meaning.
<Marker aria-label="Synced">
<MarkerIcon>
<CheckIcon />
</MarkerIcon>
</Marker>
Interactive Markers
When a marker links or triggers an action, render it as a real <button> or <a> with the render prop so it is focusable and exposes the correct role.
<Marker render={<a href="/files" />}>
<MarkerIcon>
<FileTextIcon />
</MarkerIcon>
<MarkerContent>Explored 4 files</MarkerContent>
</Marker>
API Reference
Source: react/src/components/ui/marker.tsx
Marker
The root marker element.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "border" | "separator" | "default" | The marker layout. |
render | ReactElement | function | - | Render as a different element, such as a link. |
className | string | - | Additional classes to apply to the root element. |
MarkerIcon
A decorative icon slot.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the icon slot. |
MarkerContent
The marker text content.
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Additional classes to apply to the content slot. |