A history sidebar full of rows labeled "New conversation" or UUIDs is functionally useless — users cannot find anything, so they start a new chat every time instead of continuing old ones, which defeats the point of persistence entirely. Auto-generated titles turn the archive from a dead dump into a searchable, scannable record of prior work, and they cost roughly a fraction of a cent per conversation when generated by a cheap model like gpt-4o-mini or claude-haiku.
Info because it is polish, not a functional blocker — users can still operate without titles.
After the first assistant response lands, fire a background call to a cheap model asking for a six-word title summarizing the opening exchange. Persist via UPDATE conversations SET title = $1 WHERE id = $2. Fall back to the first fifty characters of the user's opening message if the generation fails. Implement in src/lib/ai/generate-title.ts.
const { text } = await generateText({ model: openai('gpt-4o-mini'), prompt: `Six-word title for: "${firstMessage.slice(0, 200)}"` })
await updateConversationTitle(id, text.trim())
ID: ai-ux-patterns.advanced-patterns.conversation-title
Severity: info
What to look for: Count all conversations visible in the history list or sidebar. For each, enumerate the title source: auto-generated by AI, truncated from first message, user-provided, or generic default ("Untitled", "New conversation", UUID). Check for title generation logic: API calls that summarize the first exchange, first-message truncation functions, or user-rename handlers. At least 1 title generation mechanism must produce a maximum 50-character descriptive title.
Pass criteria: Conversations in the history list have human-readable titles — either auto-generated, derived from the first message, or user-provided. Titles must be descriptive and under 60 characters. Report on pass: "Title generation uses X method."
Cross-reference: For conversation persistence patterns including database schema for conversation metadata, see the Data Persistence Fundamentals in the Accessibility Fundamentals Audit.
Fail criteria: All conversations in history are titled "Untitled", "New conversation", or display a raw UUID/ID — making it impossible to identify conversations at a glance.
Skip (N/A) when: Same as conversation-organization — single-session application with no persisted history.
Detail on fail: "Conversation history present but titles default to 'New conversation' or UUID — no auto-title generation or first-message truncation logic found".
Remediation: Auto-title is a small but high-impact quality-of-life feature. A simple approach:
// After first AI response, generate a title
const generateTitle = async (firstMessage: string) => {
const { text } = await generateText({
model: openai('gpt-4o-mini'), // Use cheap model for titles
prompt: `Generate a short, descriptive title (max 6 words) for a conversation that starts with: "${firstMessage.slice(0, 200)}"`,
})
await updateConversationTitle(conversationId, text.trim())
}
Or simply use the first 50 characters of the first message:
const title = firstUserMessage.slice(0, 50) + (firstUserMessage.length > 50 ? '...' : '')