inline-reference
v0.2.1
Published
**inline-reference** is a small React component library for building **inline mentions, slash commands, and tag references** – similar to what you see in editors like Notion, Linear, or Cursor.
Readme
inline-reference
inline-reference is a small React component library for building inline mentions, slash commands, and tag references – similar to what you see in editors like Notion, Linear, or Cursor.
It lets you:
- Type a trigger (like
@,/, or#) - Show a dropdown positioned at the cursor
- Insert a “pill” reference into a rich text input
- Read structured segments (plain text vs references) from the editor
Designed to work nicely with shadcn/ui and Tailwind CSS, but usable in any React app.
Installation
npm install inline-reference @floating-ui/react
# or
pnpm add inline-reference @floating-ui/reactPeer dependencies (expected to be installed in your app):
react>= 18react-dom>= 18@floating-ui/react>= 0.27
Scaffold a shadcn-style local file
If you prefer true shadcn-style source code in your own project instead of importing directly from the package, you can scaffold a local file:
npm install inline-reference @floating-ui/react
npx inline-reference addThis will create:
components/ui/inline-reference.tsx(ifcomponents/uiexists or is created), orsrc/components/ui/inline-reference.tsx(if your project usessrc/components/ui)
After that you can import from your own components/ui directory:
import {
InlineReference,
InlineReferenceInput,
InlineReferenceContent,
InlineReferenceList,
InlineReferenceItem,
InlineReferenceEmpty,
InlineReferenceGroup,
InlineReferenceSeparator,
} from "@/components/ui/inline-reference"If the file already exists and you want to overwrite it with the latest version from the package, run:
npx inline-reference add --forceQuick start
Basic “mention someone with @” example:
import * as React from "react"
import {
InlineReference,
InlineReferenceInput,
InlineReferenceContent,
InlineReferenceList,
InlineReferenceItem,
InlineReferenceEmpty,
type InlineReferenceItemData,
} from "inline-reference"
const people: InlineReferenceItemData[] = [
{ id: "1", label: "John Doe", role: "Engineer" },
{ id: "2", label: "Jane Smith", role: "Designer" },
]
export function CommentEditor() {
return (
<InlineReference>
<InlineReferenceInput placeholder="Type @ to mention someone..." />
<InlineReferenceContent trigger="@" items={people}>
<InlineReferenceEmpty>No people found.</InlineReferenceEmpty>
<InlineReferenceList>
{(item) => (
<InlineReferenceItem key={item.id} value={item}>
<div className="flex items-center gap-2">
<div className="flex size-6 items-center justify-center rounded-full bg-primary/10 text-xs font-medium">
{item.label.charAt(0)}
</div>
<div>
<div className="text-sm font-medium">{item.label}</div>
{item.role && (
<div className="text-xs text-muted-foreground">
{String(item.role)}
</div>
)}
</div>
</div>
</InlineReferenceItem>
)}
</InlineReferenceList>
</InlineReferenceContent>
</InlineReference>
)
}Multiple triggers (@, /, #, …)
You can render multiple InlineReferenceContent blocks under a single InlineReference and use different triggers for different data sets:
const people = [
{ id: "1", label: "John Doe" },
{ id: "2", label: "Jane Smith" },
]
const commands = [
{ id: "file", label: "file", description: "Attach a file" },
{ id: "code", label: "code", description: "Insert a code block" },
]
const tags = [
{ id: "bug", label: "bug" },
{ id: "feature", label: "feature" },
]
export function MultiTriggerEditor() {
return (
<InlineReference>
<InlineReferenceInput placeholder="Type @, /, or #..." />
<InlineReferenceContent trigger="@" items={people}>
<InlineReferenceEmpty>No people found.</InlineReferenceEmpty>
<InlineReferenceList>
{(item) => (
<InlineReferenceItem key={item.id} value={item}>
@{item.label}
</InlineReferenceItem>
)}
</InlineReferenceList>
</InlineReferenceContent>
<InlineReferenceContent trigger="/" items={commands}>
<InlineReferenceEmpty>No commands found.</InlineReferenceEmpty>
<InlineReferenceList>
{(item) => (
<InlineReferenceItem key={item.id} value={item}>
<span className="text-sm">/{item.label}</span>
<span className="ml-auto text-xs text-muted-foreground">
{String(item.description)}
</span>
</InlineReferenceItem>
)}
</InlineReferenceList>
</InlineReferenceContent>
<InlineReferenceContent trigger="#" items={tags}>
<InlineReferenceEmpty>No tags found.</InlineReferenceEmpty>
<InlineReferenceList>
{(item) => (
<InlineReferenceItem key={item.id} value={item}>
#{item.label}
</InlineReferenceItem>
)}
</InlineReferenceList>
</InlineReferenceContent>
</InlineReference>
)
}Controlled mode (reading structured content)
If you want to save the editor contents as structured data (text segments and reference segments), use the controlled API:
import { type Segment } from "inline-reference"
export function ControlledEditor() {
const [segments, setSegments] = React.useState<Segment[]>([])
return (
<div className="space-y-3">
<InlineReference value={segments} onValueChange={setSegments}>
<InlineReferenceInput placeholder="Type @ to mention someone..." />
{/* ...InlineReferenceContent blocks... */}
</InlineReference>
<pre className="text-xs bg-muted rounded-md p-3 max-h-64 overflow-auto">
{JSON.stringify(segments, null, 2)}
</pre>
</div>
)
}Segment is a union of:
- Text segments –
{ type: "text"; value: string } - Reference segments –
{ type: "reference"; trigger; value; label; data? }
so you can store and replay the editor content however you like.
Styling and design tokens
This library ships with Tailwind / shadcn-style classes baked into the components:
- Uses tokens like
bg-popover,bg-accent,text-muted-foreground,border-input, etc. - Expects you to have a Tailwind + CSS variable setup similar to a standard shadcn/ui app.
To customize styles:
- Pass
classNametoInlineReferenceInput,InlineReferenceContent,InlineReferenceItem, etc. - Override the
.inline-reference-pillclass in your CSS to change how pills look.
Example (global CSS):
.inline-reference-pill {
border-radius: 9999px;
background: color-mix(in srgb, var(--accent) 70%, transparent);
}API surface
Components
InlineReference– root provider, wraps the editor and dropdowns.InlineReferenceInput– thecontentEditableinput.InlineReferenceContent– positions and renders a dropdown for a specific trigger.InlineReferenceList– renders a scrollable list of items.InlineReferenceItem– a selectable item row.InlineReferenceEmpty– rendered when there are no matching items.InlineReferenceGroup– optional grouping/heading within a dropdown.InlineReferenceSeparator– simple separator line within the dropdown.
Types
SegmentTextSegmentReferenceSegmentInlineReferenceItemData
Development / demo app
This repo also includes a small Next.js demo app that showcases:
- Basic mentions with
@ - Slash commands with
/ - Tags with
# - File references
- Controlled mode with live
segmentsJSON
To run the demo locally:
npm install
npm run devThen open http://localhost:3000 in your browser.
