Installation
pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/marker.jsonInstalls to @/components/ui/marker.
Manual installation
Install the dependencies:
pnpm add @base-ui/react@^1.8.0 class-variance-authority@^0.7.1Copy the source into your project:
import { mergeProps } from '@base-ui/react/merge-props'
import { useRender } from '@base-ui/react/use-render'
import { cva, type VariantProps } from 'class-variance-authority'
import type * as React from 'react'
import { cn } from '@/lib/utils'
const markerVariants = cva(
"group/marker relative flex min-h-4 w-full items-center gap-2 text-left text-sm text-muted-foreground [&_svg:not([class*='size-'])]:size-4 [a]:underline [a]:underline-offset-3 [a]:hover:text-foreground",
{
variants: {
variant: {
default: '',
separator:
'before:mr-1 before:h-px before:min-w-0 before:flex-1 before:bg-border after:ml-1 after:h-px after:min-w-0 after:flex-1 after:bg-border',
border: 'border-b border-border pb-2',
},
},
},
)
function Marker({
className,
variant = 'default',
render,
...props
}: useRender.ComponentProps<'div'> & VariantProps<typeof markerVariants>) {
return useRender({
defaultTagName: 'div',
props: mergeProps<'div'>(
{
className: cn(markerVariants({ variant, className })),
},
props,
),
render,
state: {
slot: 'marker',
variant,
},
})
}
function MarkerIcon({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="marker-icon"
aria-hidden="true"
className={cn("size-4 shrink-0 [&_svg:not([class*='size-'])]:size-4", className)}
{...props}
/>
)
}
function MarkerContent({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="marker-content"
className={cn(
'min-w-0 wrap-break-word group-data-[variant=separator]/marker:flex-none group-data-[variant=separator]/marker:text-center *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground',
className,
)}
{...props}
/>
)
}
export { Marker, MarkerIcon, MarkerContent, markerVariants }Displays inline status updates, system notes and labelled separators in a conversation or activity log, such as "Case registered" or a date divider. Marker comes in default (inline), border (bordered row) and separator (centred label with divider lines) variants.
Features
- Variants: default, separator and border
- Optional decorative icon slot
- Supports links inside the content
- Works with Spinner and the shimmer utility for progress
renderto render as another element, e.g.render={<output />}
Usage
import { Marker, MarkerContent, MarkerIcon } from '@/components/ui/marker'
import { Check } from 'lucide-react'
export function CaseRegistered() {
return (
<Marker>
<MarkerIcon>
<Check />
</MarkerIcon>
<MarkerContent>Case 2026-04812 was registered</MarkerContent>
</Marker>
)
}Examples
Default
An inline status note.
Separator
Centred date labels and a closing note with divider lines.
Border
Rows separated by a bottom border.
Case History
Separator markers group the events of a case by day, with inline markers for each event and its time.
Progress
A completed step followed by a progress marker rendered as an output element.
With Link
A marker containing a link.
In Conversation
Markers mixed with messages in a conversation.
Accessibility
- Marker is presentational by default; MarkerIcon is aria-hidden
- For live progress updates render it as an output element or add role="status"
- Use a real link or button (via the
renderprop) for interactive markers - Keep the text meaningful on its own without the icon