Suggested Prompts

Display suggested starter prompts in your AI chat to onboard users faster. Configurable suggestion components for React, built into assistant-ui.

Suggestions are predefined prompts that show users what your assistant can do. They appear on the welcome screen while the thread is empty, and clicking one either fills the composer or sends the message.

Quick Start

Configure suggestions using the Suggestions() API in your runtime provider:

import { AuiConfig, Tools, Suggestions } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";

function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
  const runtime = useChatRuntime();

  const config = AuiConfig({
    tools: Tools({ toolkit: myToolkit }),
    suggestions: Suggestions([
      "What can you help me with?",
      "Tell me a joke",
      "Explain quantum computing",
    ]),
  });

  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      {children}
    </AssistantRuntimeProvider>
  );
}

Suggestion Format

Suggestions can be provided as either strings or objects with title, label, and prompt:

Simple Strings

AuiConfig({
  suggestions: Suggestions([
    "What's the weather today?",
    "Help me write an email",
    "Explain React hooks",
  ]),
});

Objects with Title and Description

For more detailed suggestions with separate display text and prompts:

AuiConfig({
  suggestions: Suggestions([
    {
      title: "Weather",
      label: "in San Francisco",
      prompt: "What's the weather in San Francisco?",
    },
    {
      title: "React Hooks",
      label: "useState and useEffect",
      prompt: "Explain React hooks like useState and useEffect",
    },
    {
      title: "Travel Tips",
      label: "for Tokyo",
      prompt: "Give me travel tips for visiting Tokyo",
    },
  ]),
});

Displaying Suggestions

The default Thread component from the shadcn registry already includes suggestion rendering. The suggestions are displayed in the welcome screen when the thread is empty.

Customizing Suggestion Display

To customize how suggestions are displayed, edit your Thread component. Wrap the suggestions in AuiIf so they only appear when the thread is empty:

import {
  ThreadPrimitive,
  SuggestionPrimitive,
  AuiIf,
} from "@assistant-ui/react";

const ThreadWelcome = () => {
  return (
    <AuiIf condition={(s) => s.thread.isEmpty}>
      <div className="flex flex-col items-center justify-center">
        <h1>Welcome!</h1>
        <p>How can I help you today?</p>

        <div className="grid grid-cols-2 gap-2">
          <ThreadPrimitive.Suggestions>
            {() => <SuggestionItem />}
          </ThreadPrimitive.Suggestions>
        </div>
      </div>
    </AuiIf>
  );
};

const SuggestionItem = () => {
  return (
    <SuggestionPrimitive.Trigger send asChild>
      <button className="rounded-lg border p-3 hover:bg-muted">
        <div className="font-medium">
          <SuggestionPrimitive.Title />
        </div>
        <div className="text-muted-foreground text-sm">
          <SuggestionPrimitive.Description />
        </div>
      </button>
    </SuggestionPrimitive.Trigger>
  );
};

Dismissal

Suggestions dismiss once the user sends a message, because thread.isEmpty becomes false. To dismiss them without sending (for example, after a user clicks away), keep a local boolean and combine it with the AuiIf condition or a plain conditional render.

Suggestion Primitives

The primitives available for rendering suggestions are ThreadPrimitive.Suggestions, ThreadPrimitive.SuggestionByIndex, SuggestionPrimitive.Title, SuggestionPrimitive.Description, and SuggestionPrimitive.Trigger. Use ThreadPrimitive.SuggestionByIndex to place one suggestion slot yourself instead of iterating all of them. For the full prop reference and usage patterns, see the Suggestion primitive docs.

Runtime driven suggestions

The static Suggestions(...) API covers welcome screens. For follow up prompts that depend on the conversation, a tool result, or your backend, push suggestions through the runtime itself. They land on thread.suggestions, and when no static Suggestions(...) configuration is set, the suggestions scope derives from them, so ThreadPrimitive.Suggestions renders them as well. Runtime suggestions use the ThreadSuggestion shape: a required prompt plus optional title and label. When title is omitted it falls back to the prompt, and an omitted label renders as empty, so a prompt-only runtime suggestion looks exactly like a plain string suggestion.

Local runtime: SuggestionAdapter

Pass a suggestion adapter to useLocalRuntime. Its generate function runs after every assistant turn and may return a promise or an async generator for streaming updates.

import { useLocalRuntime, type SuggestionAdapter } from "@assistant-ui/react";

const suggestionAdapter: SuggestionAdapter = {
  async generate({ messages }) {
    const response = await fetch("/api/follow-ups", {
      method: "POST",
      body: JSON.stringify({ messages }),
    });
    const data: { prompt: string }[] = await response.json();
    return data;
  },
};

const runtime = useLocalRuntime(myChatModel, {
  adapters: { suggestion: suggestionAdapter },
});

For LLM-powered follow ups, use createSuggestionAdapter. It builds a deterministic transcript prompt from recent messages and calls your complete function. Forward the optional signal so in-flight generation can be cancelled when a new run starts.

import { createSuggestionAdapter, useLocalRuntime } from "@assistant-ui/react";

const suggestionAdapter = createSuggestionAdapter({
  async complete({ prompt, signal }) {
    const response = await fetch("/api/suggestions", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ prompt }),
      signal,
    });
    const data: string[] = await response.json();
    return data;
  },
  count: 3,
});

const runtime = useLocalRuntime(myChatModel, {
  adapters: { suggestion: suggestionAdapter },
});

External store runtime

useExternalStoreRuntime exposes a suggestions field, so you can drive follow ups straight from your application state.

const [suggestions, setSuggestions] = useState<ThreadSuggestion[]>([]);

const runtime = useExternalStoreRuntime({
  messages,
  onNew,
  suggestions,
});

// Push a follow up after a tool result, a stream chunk, or any app event.
setSuggestions([{ prompt: "Summarize this case" }]);

AI SDK runtime

useChatRuntime and useAISDKRuntime accept the same suggestions field and forward it to the underlying external store.

const [suggestions, setSuggestions] = useState<ThreadSuggestion[]>([]);

const runtime = useChatRuntime({ suggestions });

They also accept the same suggestion adapter as the local runtime under adapters, so one adapter works across both runtimes. When set, the adapter owns thread.suggestions after each run settles, and any static suggestions option is ignored. Pair it with createSuggestionAdapter for built-in prompt construction:

import { createSuggestionAdapter } from "@assistant-ui/react";
import { useChatRuntime } from "@assistant-ui/ai-sdk";

const runtime = useChatRuntime({
  adapters: {
    suggestion: createSuggestionAdapter({
      async complete({ prompt, signal }) {
        const response = await fetch("/api/suggestions", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ prompt }),
          signal,
        });
        return response.json();
      },
    }),
  },
});

Local, data-stream, and AI SDK runtimes treat a keyless adapter, inline or from createSuggestionAdapter, as one logical adapter across rerenders. If changing an adapter's inputs should immediately regenerate suggestions for an already-settled response, pass a key that changes with those inputs. Switching between separately created keyless adapters also requires changing the key; object or factory identity alone does not select a replacement.

Example route that returns suggestion strings with the AI SDK:

// app/api/suggestions/route.ts
import { generateObject } from "ai";
import { z } from "zod";

export async function POST(req: Request) {
  const { prompt } = await req.json();

  const { object } = await generateObject({
    model: yourModel,
    schema: z.object({
      suggestions: z.array(z.string()).min(1).max(5),
    }),
    prompt,
  });

  return Response.json(object.suggestions);
}

The local runtime clears its suggestions when a new run starts. External store runtimes keep whatever state you push, so you control the lifetime. With adapters.suggestion on the AI SDK runtime, suggestions clear on run start and regenerate after settle, matching the local runtime.

Rendering runtime suggestions

ThreadPrimitive.Suggestions renders runtime suggestions unless a static Suggestions(...) configuration is set. The shadcn registry ships ThreadFollowupSuggestions for the common single line chip layout. For a custom layout, read the array yourself:

import { useAuiState, ThreadPrimitive, AuiIf } from "@assistant-ui/react";

function FollowUps() {
  const suggestions = useAuiState((s) => s.thread.suggestions);
  return (
    <AuiIf condition={(s) => !s.thread.isEmpty && !s.thread.isRunning}>
      <div className="flex gap-2">
        {suggestions.map((s, i) => (
          <ThreadPrimitive.Suggestion key={i} prompt={s.prompt} send>
            {s.title ?? s.prompt}
            {s.label && (
              <span className="text-muted-foreground ms-1">{s.label}</span>
            )}
          </ThreadPrimitive.Suggestion>
        ))}
      </div>
    </AuiIf>
  );
}

Reacting to application state

Compute the static suggestion list from your application state:

import { useMemo } from "react";

function MyRuntimeProvider({ children }: { children: React.ReactNode }) {
  const runtime = useChatRuntime();
  const user = useUser(); // Your user hook

  const suggestions = useMemo(() => {
    if (user.isPremium) {
      return [
        "Analyze my business data",
        "Generate a detailed report",
        "Create a custom workflow",
      ];
    }
    return [
      "What can you do?",
      "Tell me a joke",
      "Help me get started",
    ];
  }, [user.isPremium]);

  const config = AuiConfig({
    tools: Tools({ toolkit: myToolkit }),
    suggestions: Suggestions(suggestions),
  });

  return (
    <AssistantRuntimeProvider runtime={runtime} config={config}>
      {children}
    </AssistantRuntimeProvider>
  );
}

Best Practices

  • Write short prompts that a user can read at a glance.
  • Cover different use cases so the suggestions show what the assistant can do.
  • Use the object format when the display text should differ from the prompt.
  • Offer three to six suggestions.

Switching from ThreadPrimitive.Suggestion

If your codebase uses the inline ThreadPrimitive.Suggestion component (which renders one suggestion at a time with hardcoded prompt / send props), you can move to the Suggestions() API and configure them in one place. The inline component is still supported.

Inline form

<ThreadPrimitive.Suggestion
  prompt="What's the weather?"
  send
/>

Suggestions() form

  1. Configure suggestions in your runtime provider's config:
AuiConfig({
  suggestions: Suggestions(["What's the weather?"]),
});
  1. Display suggestions using the primitives:
<ThreadPrimitive.Suggestions>
  {() => <SuggestionItem />}
</ThreadPrimitive.Suggestions>

The Suggestions() form defines the prompts once in your runtime provider, separate from the components that render them, and follows the same pattern as the Tools API.