New users need to understand what the assistant can do and how to start.
Updated September 12, 2026
Problem
An empty conversation needs to help newcomers understand the assistant without slowing down people who already use it.
Solution
Briefly explain what the assistant can do and what content it can access. Include a few relevant prompt starters and the limits people need to know before asking.
Keep the message field prominent. Avoid tours or instructions that people must dismiss every time they start a conversation.
Give a short explanation of how conversation data is used, with a link to details. Provide access to earlier conversations. An empty history can also belong to an experienced user, so avoid assuming every visit is someone’s first.
Use when
People regularly return to an assistant’s empty conversation screen.
Don’t use when
The assistant is summoned to a specific object and dismissed, like an inline rewrite or a command-bar invocation. There the selection is the context, the invocation carries the intent, and a teaching screen in front of it’s an obstacle.
Trade-offs
Help text competes with the message field. Too little guidance encourages guessing, while too much becomes repetitive. Capability and privacy descriptions need updating when the product changes.
Checklist
Does the screen say what this assistant can see, not just what it can do?
Does it state a limitation, and does that survive review by someone protecting adoption numbers?
Is the data question answered here rather than in settings?
What does this look like on the four-hundredth visit?
Does anything require dismissing, and does the dismissal persist?
Is the composer the visual centre?
Is it pleasant for a returning user with an empty history?
Who owns updating the capability claims when the product changes?
Does it work at a phone width with the keyboard up?
Is there a path from here to previous conversations?
Compare
ChatGPT keeps the screen close to empty with the composer centred and a small rotating set of examples, betting that the product is well enough known that explanation costs more than it returns. Claude changes the screen inside a project, where it describes the project’s material and makes the assistant’s scope the subject rather than the product’s features. Perplexity fills the empty state with live example queries and trending searches, It teaches question shape, and it doubles as something to browse when the viewer hasn’t got a question yet. Microsoft Copilot inside an Office application spends the screen on what it can reach in the current document and tenant, which is the right call for an assistant whose main unknown is its scope rather than its ability.
Related
Prompt starters demonstrate useful requests. Composer provides the main action, and Conversation history links to previous work.
Refusal should be consistent with the limits described here. Memory chip shows when the assistant saves information for later.
Wireframe — the pattern's anatomy, not any one product's version of it
Implementation
An empty conversation composed around the input: what the assistant can see, a few examples, a stated limitation, and what happens to what you type. Nothing here needs dismissing, because everyone who is not new sees it four hundred times.
it can read pages you have access to, not the whole company
It gets dates and figures wrong. Check anything you act on.
Conversations are kept in this workspace and visible to admins.
FirstRunState.tsxThe four lines are props, so the screen cannot ship without them. An example fills the input rather than sending it.
import { useRef, useState } from "react";import { ArrowUp } from "lucide-react";import { Button } from "@/components/ui/button";import { Textarea } from "@/components/ui/textarea";type Props = { /** One line on what the assistant can see. Scope is the thing a viewer * cannot guess, and the reason to use this over a chatbot. */ scope: string; /** Two or three. More than that is a menu, and a menu is a thing to read. */ examples: string[]; /** Stated up front. It reads as an apology and prevents the failure that * actually loses people. */ limitation: string; /** What happens to what gets typed. Asked at the moment of first input, * not on a settings page. */ dataNotice: string; onSend: (text: string) => void;};export function FirstRunState({ scope, examples, limitation, dataNotice, onSend }: Props) { const [draft, setDraft] = useState(""); const ref = useRef<HTMLTextAreaElement>(null); const send = () => { const text = draft.trim(); if (!text) return; onSend(text); setDraft(""); }; // An example fills the input rather than sending. A newcomer is here to // learn what a question looks like, and a question they can edit teaches // that better than one that fires on click. const useExample = (text: string) => { setDraft(text); ref.current?.focus(); }; return ( <div className="rounded-lg border bg-card p-5 text-center"> <p className="text-base text-card-foreground">Ask about anything in this workspace</p> <p className="mt-1.5 text-xs text-muted-foreground">{scope}</p> <div className="mt-4 flex flex-wrap justify-center gap-2"> {examples.map((e) => ( <Button key={e} variant="outline" size="sm" className="rounded-full" onClick={() => useExample(e)}> {e} </Button> ))} </div> {/* The input is the centre. Everything else on this screen has to be quieter than it, so a veteran can skip the whole composition. */} <div className="mx-auto mt-5 flex w-[464px] max-w-full items-end gap-2 rounded-lg border bg-background p-2"> <Textarea ref={ref} value={draft} onChange={(e) => setDraft(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault(); send(); } }} rows={1} placeholder="Ask a question" aria-label="Ask a question" className="min-h-0 resize-none border-0 px-2 py-1.5 text-left shadow-none focus-visible:ring-0" /> <Button size="sm" onClick={send} disabled={!draft.trim()}> <ArrowUp className="size-4" /> send </Button> </div> <p className="mt-5 text-xs text-status-warn">{limitation}</p> <p className="mt-2.5 text-xs text-muted-foreground">{dataNotice}</p> </div> );}
demo.tsxHow it is called: the four lines as props. Send shows what went.
import { useState } from "react";import { FirstRunState } from "./FirstRunState";/** The four lines a first run has to state, as props. Send echoes what was sent. */export default function Demo() { const [sent, setSent] = useState<string | null>(null); return ( <div> <FirstRunState scope="it can read pages you have access to, not the whole company" examples={["summarise a page", "find a decision we made", "draft from these notes"]} limitation="It gets dates and figures wrong. Check anything you act on." dataNotice="Conversations are kept in this workspace and visible to admins." onSend={setSent} /> {sent && <p className="mt-3 text-xs text-muted-foreground">sent: “{sent}”</p>} </div> );}
What it renders. Identical markup in both panes, with only the token values changing.