Elements

Elements · AUI connected · AUI

Directive text

A message renderer that turns mention directives into inline, runtime-aware chips.

User message
Use Get Weather to check today's forecast in Tokyo.
Another example
Ask Search for recent updates on Calendar.
fig. 01

Installation

npx shadcn@latest add "@assistant-ui/directive-text"
First time? Set up a runtime

Runtime components read their state from an assistant-ui runtime. Add one to an existing project:

npx assistant-ui@latest init

Then wrap your app in a runtime provider:

import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";

export default function App() {
  const runtime = useChatRuntime({
    transport: new AssistantChatTransport({ api: "/api/chat" }),
  });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      {/* your components */}
    </AssistantRuntimeProvider>
  );
}

The installation guide covers new projects, templates, and API routes.

Directive text parses a message's plain text for directive syntax and renders each one as an inline chip instead of raw markup, leaving ordinary text untouched. With a runtime it plugs into MessagePrimitive.Parts as the text renderer and understands assistant-ui's own directive format out of the box; standalone you supply your own parser and get the same chip rendering with no runtime underneath it.

Getting started

Register the renderer

DirectiveText is pre-wired with assistant-ui's default directive format. Pass it as the Text renderer on MessagePrimitive.Parts wherever directive chips should appear.

components/assistant-ui/elements/thread.aui.tsx
import { DirectiveText } from "@/components/assistant-ui/elements/directive-text.aui";
import { MessagePrimitive } from "@assistant-ui/react";

function UserMessage() {
  return (
    <MessagePrimitive.Root>
      <MessagePrimitive.Parts components={{ Text: DirectiveText }} />
    </MessagePrimitive.Root>
  );
}

A message with no directive syntax renders exactly as plain text; nothing extra is added when there's nothing to parse.

Pair this with Composer trigger popover: a mention or slash command selected there inserts the same directive syntax into the sent message, and DirectiveText renders it back as a chip.

Anatomy

A message whose text has no directive matches renders as the bare string, with no wrapper element. Once there is at least one match, the text splits into a sequence of runs:

<>
  <span>{textBetweenDirectives}</span>
  <span data-slot="directive-text-chip" data-directive-type={type} data-directive-id={id} aria-label={`${type}: ${label}`}>
    <Icon />
    {label}
  </span>
  {/* repeated per segment, in order */}
</>

Each chip's icon comes from iconMap[type], falling back to fallbackIcon when the map has no entry for that type, or no icon at all when neither is given. The chip is a fixed, secondary-styled badge with no className override; a renderer that needs different styling calls formatter.parse itself and maps the segments to its own markup instead of using createDirectiveText.

Examples

Assistant-ui's default directive format

With no custom parser, both DirectiveText and a createDirectiveText call with unstable_defaultDirectiveFormatter understand :type[label]{name=id}; the {name=…} attribute is omitted when id equals label, so :tool[Search] and :tool[Search]{name=web-search} both parse, the second with a different id.

"Ask :tool[Search]{name=web-search} to look this up."
// → "Ask ", a chip labeled "Search" (type "tool", id "web-search"), " to look this up."

Map directive types to icons

iconMap and fallbackIcon work the same whether the formatter is the default or your own; keys match each segment's type.

import { WrenchIcon, SlashIcon, SparklesIcon } from "lucide-react";

createDirectiveText(unstable_defaultDirectiveFormatter, {
  iconMap: { tool: WrenchIcon, command: SlashIcon },
  fallbackIcon: SparklesIcon,
});

unstable_useMentionAdapter's model-context tools default to type: "tool", and unstable_useSlashCommandAdapter's commands default to type: "command", so this pair of keys covers both out of the box.

API reference

DirectiveText

ExportTypeDescription
DirectiveTextTextMessagePartComponentReady to use; parses assistant-ui's default :type[label]{name=id} syntax. Pass as MessagePrimitive.Parts's components.Text.
createDirectiveText(formatter, options?)(formatter, options?) => TextMessagePartComponentBuilds a Text renderer around any formatter, for a custom iconMap or fallbackIcon.

Directive segment

FieldTypeDescription
kind"text" | "mention"Which shape the segment is.
textstringThe literal run, for kind: "text".
typestringThe directive's type, for kind: "mention"; used to look up an icon.
labelstringShown inside the chip.
idstringNot shown; carried in data-directive-id.