Marker
Displays an inline status, system note, bordered row, or labeled separator in a conversation.
import { Check, Clock3, FileText, GitBranch } from "lucide-solid";
import { Example, ExampleWrapper } from "@/components/example";import { Marker, MarkerContent, MarkerIcon } from "~/components/ui/marker";import { Spinner } from "~/components/ui/spinner";
export default function MarkerDemo() { return ( <ExampleWrapper> <Example title="Default"> <div class="flex w-full flex-col gap-4"> <Marker> <MarkerContent>A default marker</MarkerContent> </Marker> <Marker> <MarkerIcon> <FileText /> </MarkerIcon> <MarkerContent>Marker with an icon</MarkerContent> </Marker> <Marker role="status"> <MarkerIcon> <Spinner /> </MarkerIcon> <MarkerContent>Compacting conversation</MarkerContent> </Marker> </div> </Example> <Example title="Variants"> <div class="flex w-full flex-col gap-4"> <Marker variant="border"> <MarkerIcon> <GitBranch /> </MarkerIcon> <MarkerContent>Switched to release-candidate</MarkerContent> </Marker> <Marker variant="separator"> <MarkerContent>Worked for 42s</MarkerContent> </Marker> <Marker variant="separator"> <MarkerIcon> <Check /> </MarkerIcon> <MarkerContent>Conversation compacted</MarkerContent> </Marker> </div> </Example> <Example title="Link"> <Marker as="a" href="#marker-demo"> <MarkerIcon> <Clock3 /> </MarkerIcon> <MarkerContent>View file activity</MarkerContent> </Marker> </Example> </ExampleWrapper> );}The Marker component displays inline conversation markers such as status updates, system notes,
bordered rows, and labeled separators. Compose it with
Message in a conversation thread.
Installation
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└── MarkerContentFeatures
- Inline marker, bordered row, and labeled separator variants.
- Decorative icon slot that is hidden from assistive tech.
- Polymorphic root via
asfor link and button markers. - Pairs with the
shimmerutility class for streaming status text. - Customizable styling through the
classprop on every part.
Variants
Use variant to switch between an inline marker, bordered row, and labeled separator.
import { Marker, MarkerContent } from "~/components/ui/marker";
export default function MarkerVariants() { return ( <div class="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> );}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 default function MarkerStatus() { return ( <div class="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 default function MarkerShimmer() { return ( <div class="flex w-full max-w-sm flex-col gap-8 py-12"> <Marker role="status"> <MarkerContent class="shimmer">Thinking...</MarkerContent> </Marker> <Marker variant="separator" role="status"> <MarkerContent class="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 default function MarkerSeparator() { return ( <div class="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 { FileText, GitBranch, Search } from "lucide-solid";
import { Marker, MarkerContent, MarkerIcon } from "~/components/ui/marker";
export default function MarkerBorder() { return ( <div class="flex w-full max-w-sm flex-col gap-3 py-12"> <Marker variant="border"> <MarkerIcon> <GitBranch /> </MarkerIcon> <MarkerContent>Switched to release-candidate</MarkerContent> </Marker> <Marker variant="border"> <MarkerIcon> <Search /> </MarkerIcon> <MarkerContent>Reviewed 8 related files</MarkerContent> </Marker> <Marker variant="border"> <MarkerIcon> <FileText /> </MarkerIcon> <MarkerContent>Opened implementation notes</MarkerContent> </Marker> </div> );}With Icon
Use MarkerIcon to render an icon alongside the content. Use flex-col to stack the icon above
the content.
import { BookOpenCheck, GitBranch, Search } from "lucide-solid";
import { Marker, MarkerContent, MarkerIcon } from "~/components/ui/marker";
export default function MarkerIconDemo() { return ( <div class="flex w-full max-w-sm flex-col gap-12 py-12"> <Marker> <MarkerIcon> <GitBranch /> </MarkerIcon> <MarkerContent>Switched to a new branch</MarkerContent> </Marker> <Marker variant="separator"> <MarkerIcon> <Search /> </MarkerIcon> <MarkerContent>Explored 4 files</MarkerContent> </Marker> <Marker class="flex-col"> <MarkerIcon> <BookOpenCheck /> </MarkerIcon> <MarkerContent>Syncing completed</MarkerContent> </Marker> </div> );}Links and Buttons
Turn a marker into a link or button with the as prop on Marker.
import { GitBranch, RotateCcw } from "lucide-solid";import { toast } from "solid-sonner";
import { Marker, MarkerContent, MarkerIcon } from "~/components/ui/marker";import { Toaster } from "~/components/ui/toast";
export default function MarkerLinkButton() { return ( <> <Toaster /> <div class="flex w-full max-w-sm flex-col gap-8 py-12"> <Marker as="a" href="#links-and-buttons"> <MarkerIcon> <GitBranch /> </MarkerIcon> <MarkerContent>View the pull request</MarkerContent> </Marker> <Marker as="button" type="button" class="transition-colors hover:text-foreground" onClick={() => toast("You clicked the revert button")} > <MarkerIcon> <RotateCcw /> </MarkerIcon> <MarkerContent>Revert this change</MarkerContent> </Marker> </div> </> );}import { Marker, MarkerContent } from "~/components/ui/marker";
export function MarkerLinkDemo() { return ( <Marker as="a" href="#"> <MarkerContent>View the pull request</MarkerContent> </Marker> );}Accessibility
Marker is presentational by default. The correct semantics depend on how you use it, so choose
the role based on intent rather than relying on a single 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 forwards role to the underlying
element.
<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. The divider lines are decorative CSS pseudo-elements, and the text is announced as ordinary content.
<Marker variant="separator"> <MarkerContent>Today</MarkerContent></Marker>Note: Do not add role="separator" to a labeled divider. A separator takes its accessible name
from aria-label, not from its text, and its contents are treated as presentational, so the
visible label would not be announced. Reserve role="separator" for a divider with no meaningful
text.
Bordered Markers
A bordered marker keeps the same semantics as the default marker. The bottom border is decorative,
so choose role="status", as, or no role based on the marker's purpose.
<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. For an icon-only marker, provide an aria-label or visible
text so it is not announced as empty.
<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 as
prop so it is focusable and exposes the correct role. The accessible name comes from the marker
text.
<Marker as="a" href="/files"> <MarkerIcon> <FileTextIcon /> </MarkerIcon> <MarkerContent>Explored 4 files</MarkerContent></Marker>API Reference
Marker
The root marker element. The file also exports markerVariants for composing the marker styles
into custom components.
MarkerIcon
A decorative icon slot. Hidden from assistive tech with aria-hidden.
MarkerContent
The marker text content.