suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
  • AI
GitHubCreate
DocsComponentsPatternsInteractionsChartsBlocksAICreate
AI21
  • Overview
Conversation
  • Chat
  • Message
  • Prompt Input
  • Suggestion
  • Model Selector
  • Context
  • Checkpoint
  • Inline Citation
Agent steps
  • Chain of Thought
  • Reasoning
  • Tool
  • Confirmation
  • Task
  • Plan
  • Queue
  • Subagent
  • Sources
Output
  • Terminal
  • Stack Trace
  • Test Results
  • File Tree

suiss/ui · AI

Reasoning

A line for the agent's thinking that opens while the model thinks and closes when it is done.

import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Message, MessageContent } from "@/components/ui/message"
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"

export default function ReasoningDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-4 py-6 text-sm">
      <Message align="end">
        <MessageContent>

Reasoning is a line for the agent's thinking. It reads "Thinking" and opens while the model thinks, then reads "Thought" and closes when it is done. The thinking sits inside, and the line can show how long it took.

It is built with Trace: a TraceCollapsible whose content is the reasoning text. For the steps that follow, see Chain of Thought.

Installation

Reasoning is built with Trace.

$ pnpm dlx shadcn@latest add @esuiss/trace

Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add trace

Usage

import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"
<Trace>
  <TraceCollapsible>
    <TraceCollapsibleTrigger>
      <TraceLine>
        <TraceIcon>
          <CircleCheckIcon />
        </TraceIcon>
        <TraceTitle>Thought</TraceTitle>
      </TraceLine>
    </TraceCollapsibleTrigger>
    <TraceCollapsibleContent className="whitespace-pre-wrap">
      {text}
    </TraceCollapsibleContent>
  </TraceCollapsible>
</Trace>

Set whitespace-pre-wrap on the content so paragraph breaks in the reasoning text are kept.

Examples

Duration

Shows how long the model thought in a TraceDescription after the title. Measure it on the server and send it with the message.

import { CheckCircleIcon } from "@phosphor-icons/react"
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceDescription,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"

export default function ReasoningDuration() {
  return (
    <Trace className="max-w-sm">
      <TraceCollapsible>

AI SDK

Render each reasoning part as a line. It starts as a plain TraceLine and becomes a TraceCollapsible once text arrives, so there is nothing to open before the model has written anything. It opens while the model thinks and closes when it finishes; once the reader opens or closes it, their choice wins.

function ReasoningLine({ text, running }: { text: string; running: boolean }) {
  const [open, setOpen] = React.useState<boolean>()

  const line = (
    <TraceLine status={running ? "running" : undefined}>
      <TraceTitle>{running ? "Thinking" : "Thought"}</TraceTitle>
    </TraceLine>
  )

  if (text.length === 0) {
    return line
  }

  return (
    <TraceCollapsible open={open ?? running} onOpenChange={setOpen}>
      <TraceCollapsibleTrigger>{line}</TraceCollapsibleTrigger>
      <TraceCollapsibleContent className="whitespace-pre-wrap">
        {text}
      </TraceCollapsibleContent>
    </TraceCollapsible>
  )
}
<Trace>
  {message.parts
    .filter((part) => part.type === "reasoning")
    .map((part, index) => (
      <ReasoningLine
        key={index}
        text={part.text}
        running={streaming && part.state === "streaming"}
      />
    ))}
</Trace>

Some models send several reasoning parts in one message. Each one is a line in the same Trace.

With React Aria, pass isExpanded and onExpandedChange instead of open and onOpenChange.

API Reference

See Trace.

Accessibility

  • The line is a button with aria-expanded once there is text to show.
  • The running shimmer stops when the reader prefers reduced motion; the title still says "Thinking".
import { BrainIcon } from "phosphor-react-native"
import { View } from "react-native"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { Icon } from "@/components/ui/icon"
import { Message, MessageContent } from "@/components/ui/message"
import { Text } from "@/components/ui/text"
import { TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceIcon, TraceLine, TraceTitle } from "@/components/ui/trace"

export function AiReasoningExample() {
  return (
    <View className="w-full gap-4">
      <Message align="end">
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>Why is the checkout deploy so slow?</BubbleContent>
          </Bubble>
import EsuissUI
import PhosphorSwift
import SwiftUI

struct ReasoningExample: View {
  var body: some View {
    VStack(alignment: .leading, spacing: 16) {
      Message(align: .end) {
        MessageContent {
          Bubble(variant: .muted, align: .end) { BubbleContent("Why is the checkout deploy so slow?") }
        }
      }
      TraceCollapsible(initiallyExpanded: true) {
        TraceCollapsibleTrigger {
          TraceLine {
            TraceIcon { Ph.brain.regular }
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.adamglin.PhosphorIcons
import com.adamglin.phosphoricons.Regular
import com.adamglin.phosphoricons.regular.Brain
import com.esuiss.ui.components.Bubble
import com.esuiss.ui.components.BubbleContent
import com.esuiss.ui.components.BubbleVariant
import com.esuiss.ui.components.Icon
import com.esuiss.ui.components.Message
import com.esuiss.ui.components.MessageAlign
import com.esuiss.ui.components.MessageContent

Reasoning on mobile is built from the same parts as on the web: Trace, Message, Bubble. The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

Installation

$ pnpm dlx @esuiss/ui@latest add trace message bubble
$ pnpm dlx @esuiss/ui@latest add trace message bubble --platform swift
$ pnpm dlx @esuiss/ui@latest add trace message bubble --platform kotlin

API Reference

See Trace, Message, Bubble.

NextToolPreviousChain of Thought

On this page

  1. 01Installation
  2. 02Usage
  3. 03Examples
    • Duration
  4. 04AI SDK
  5. 05API Reference
  6. 06Accessibility
  7. 07Installation
  8. 08API Reference