# Chain of Thought

A summary line that expands into the steps an agent took, such as reads, searches and tool calls.

Source: https://suiss.app/ui/docs/ai/chain-of-thought

**React**

**Example**

```tsx
import {
  ArrowUpRightIcon,
  CheckCircleIcon,
  GlobeIcon,
} from "@phosphor-icons/react"
import type * as React from "react"

import { Badge } from "@/components/ui/badge"
import { Bubble, BubbleContent } from "@/components/ui/bubble"
import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block"
import { Message, MessageContent } from "@/components/ui/message"
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"

const results = [
  {
    title: "Image component | nextjs.org",
    url: "https://nextjs.org/docs/app/api-reference/components/image",
  },
  {
    title: "Build cache | vercel.com",
    url: "https://vercel.com/docs/deployments/troubleshoot-a-build",
  },
]

const files = ["next.config.ts", "scripts/optimize-images.ts"]

function Step({
  title,
  defaultOpen,
  children,
}: {
  title: string
  defaultOpen?: boolean
  children: React.ReactNode
}) {
  return (
    <TraceCollapsible defaultOpen={defaultOpen}>
      <TraceCollapsibleTrigger>
        <TraceLine>
          <TraceIcon>
            <CheckCircleIcon />
          </TraceIcon>
          <TraceTitle>{title}</TraceTitle>
        </TraceLine>
      </TraceCollapsibleTrigger>
      <TraceCollapsibleContent>{children}</TraceCollapsibleContent>
    </TraceCollapsible>
  )
}

export default function ChainOfThoughtDemo() {
  return (
    <div className="flex w-full max-w-sm flex-col gap-4 py-6 text-sm">
      <Message align="end">
        <MessageContent>
          <Bubble align="end" variant="muted">
            <BubbleContent>Why is the checkout deploy so slow?</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <Message>
        <MessageContent className="gap-4">
          <Trace>
            <TraceCollapsible defaultOpen>
              <TraceCollapsibleTrigger>
                <TraceLine>
                  <TraceIcon>
                    <CheckCircleIcon />
                  </TraceIcon>
                  <TraceTitle>Researched in 3 steps</TraceTitle>
                </TraceLine>
              </TraceCollapsibleTrigger>
              <TraceCollapsibleContent keepMounted>
                <Trace>
                  <Step title="Read the build log for dpl_8Kx2" defaultOpen>
                    <CodeBlock size="sm">
                      <CodeBlockContent wrap className="max-h-64">
                        {"install    41s\ncompile    38s\nimages   3m 52s"}
                      </CodeBlockContent>
                    </CodeBlock>
                  </Step>
                  <Step title="Searched the web for next.js image cache key">
                    <Trace>
                      {results.map((result) => (
                        <a
                          key={result.url}
                          href={result.url}
                          target="_blank"
                          rel="noreferrer"
                        >
                          <TraceLine>
                            <TraceIcon>
                              <GlobeIcon />
                            </TraceIcon>
                            <TraceTitle>
                              {result.title}
                              <span className="sr-only">
                                {" "}
                                (opens in a new tab)
                              </span>
                            </TraceTitle>
                            <TraceIcon>
                              <ArrowUpRightIcon />
                            </TraceIcon>
                          </TraceLine>
                        </a>
                      ))}
                    </Trace>
                  </Step>
                  <Step title="Searched the code for cacheKey">
                    <div className="flex flex-wrap gap-1.5">
                      {files.map((file) => (
                        <Badge
                          key={file}
                          variant="secondary"
                          className="font-mono"
                        >
                          {file}
                        </Badge>
                      ))}
                    </div>
                  </Step>
                </Trace>
              </TraceCollapsibleContent>
            </TraceCollapsible>
          </Trace>
          <p>
            Image optimization takes almost four minutes because its cache key
            includes the commit hash. Key it on the image directory instead.
          </p>
        </MessageContent>
      </Message>
    </div>
  )
}
```

Chain of Thought is a summary line that expands into the steps an agent took. The summary says what happened, such as "Researched in 3 steps", and each step inside it is a line of its own that can expand into its output.

It is built with [Trace](https://suiss.app/ui/docs/components/trace): a `TraceCollapsible` for the summary and a nested `Trace` for the steps. For a single tool call, see [Tool](https://suiss.app/ui/docs/ai/tool). For steps that are known up front, see [Plan](https://suiss.app/ui/docs/ai/plan).

## Installation

Chain of Thought is built with [Trace](https://suiss.app/ui/docs/components/trace), [Code Block](https://suiss.app/ui/docs/components/code-block) and [Badge](https://suiss.app/ui/docs/components/badge).

```bash title="Terminal"
npx shadcn@latest add @esuiss/trace @esuiss/code-block @esuiss/badge
```

Or use the [suiss/ui CLI](https://suiss.app/ui/docs/cli):

```bash title="Terminal"
npx @esuiss/ui@latest add trace code-block badge
```

## Usage

```tsx
import {
  Trace,
  TraceCollapsible,
  TraceCollapsibleContent,
  TraceCollapsibleTrigger,
  TraceIcon,
  TraceLine,
  TraceTitle,
} from "@/components/ui/trace"
```

```tsx
<Trace>
  <TraceCollapsible defaultOpen>
    <TraceCollapsibleTrigger>
      <TraceLine>
        <TraceIcon>
          <CircleCheckIcon />
        </TraceIcon>
        <TraceTitle>Researched in 3 steps</TraceTitle>
      </TraceLine>
    </TraceCollapsibleTrigger>
    <TraceCollapsibleContent keepMounted>
      <Trace>
        <TraceLine>
          <TraceIcon>
            <CircleCheckIcon />
          </TraceIcon>
          <TraceTitle>Read the build log for dpl_8Kx2</TraceTitle>
        </TraceLine>
        <TraceLine>
          <TraceIcon>
            <CircleCheckIcon />
          </TraceIcon>
          <TraceTitle>Searched the code for cacheKey</TraceTitle>
        </TraceLine>
      </Trace>
    </TraceCollapsibleContent>
  </TraceCollapsible>
</Trace>
```

Set `keepMounted` on the summary's `TraceCollapsibleContent` so the steps keep their open state while the summary is closed.

### Steps

Each step is a `TraceLine`, or a `TraceCollapsible` when it has output to show. Set its `status` and write the title in the matching tense.

| Step | `status` | Title |
| --- | --- | --- |
| Running | `"running"` | Searching for … |
| Done | none | Searched for … |
| Failed | `"error"` | Failed to search for … |

Set the summary's status and title from its steps: `running` while any step runs, `error` when one failed, and a count such as "Researched in 3 steps, 1 failed" once they are done.

### Search results

When a step runs a web search, show each result as a link row, or show the hostnames as a `Badge` each:

```tsx
<TraceCollapsibleContent className="flex flex-wrap gap-1.5">
  {results.map((result) => (
    <Badge key={result.url} variant="secondary">
      {new URL(result.url).hostname}
    </Badge>
  ))}
</TraceCollapsibleContent>
```

## AI SDK

Render each tool part as a step. The summary is running while tool parts stream and before the reply text starts. It opens while the steps run and keeps the reader's choice once they open or close it.

```tsx
const [open, setOpen] = React.useState<boolean>()
const running = streaming && !answering
const failed = steps.filter((part) => part.state === "output-error").length

<TraceCollapsible open={open ?? running} onOpenChange={setOpen}>
  <TraceCollapsibleTrigger>
    <TraceLine status={running ? "running" : failed > 0 ? "error" : undefined}>
      <TraceTitle>
        {running ? "Researching" : `Researched in ${steps.length} steps`}
      </TraceTitle>
    </TraceLine>
  </TraceCollapsibleTrigger>
  <TraceCollapsibleContent keepMounted>
    <Trace>
      {steps.map((part) => (
        <StepLine key={part.toolCallId} part={part} streaming={streaming} />
      ))}
    </Trace>
  </TraceCollapsibleContent>
</TraceCollapsible>
```

`StepLine` renders one tool part as a line, as in [Tool](https://suiss.app/ui/docs/ai/tool#states).

## API Reference

See [Trace](https://suiss.app/ui/docs/components/trace#api-reference), [Code Block](https://suiss.app/ui/docs/components/code-block#api-reference) and [Badge](https://suiss.app/ui/docs/components/badge#api-reference).

## Accessibility

- The summary and each expandable step are buttons with `aria-expanded`.
- Say the state in each title, such as "Searching" or "Failed to search", since status is otherwise shown with color and motion.
- Add "(opens in a new tab)" with `sr-only` to links that open a new tab.

**React Native, SwiftUI, Jetpack Compose**

```tsx
import { FileTextIcon, GlobeIcon, MagnifyingGlassIcon } 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 { Trace, TraceCollapsible, TraceCollapsibleContent, TraceCollapsibleTrigger, TraceDescription, TraceIcon, TraceLine, TraceTitle } from "@/components/ui/trace"

export function AiChainOfThoughtExample() {
  return (
    <View className="w-full gap-4">
      <Message align="end">
        <MessageContent>
          <Bubble variant="muted">
            <BubbleContent>Why is the checkout deploy so slow?</BubbleContent>
          </Bubble>
        </MessageContent>
      </Message>
      <TraceCollapsible defaultOpen>
        <TraceCollapsibleTrigger>
          <TraceLine>
            <TraceTitle>Researched in 3 steps</TraceTitle>
          </TraceLine>
        </TraceCollapsibleTrigger>
        <TraceCollapsibleContent>
          <Trace>
            <TraceLine>
              <TraceIcon>
                <Icon as={FileTextIcon} />
              </TraceIcon>
              <TraceTitle>Read the build log for dpl_8Kx2</TraceTitle>
            </TraceLine>
            <TraceLine>
              <TraceIcon>
                <Icon as={GlobeIcon} />
              </TraceIcon>
              <TraceTitle>
                Searched the web for next.js image cache key
              </TraceTitle>
              <TraceDescription>2 sources</TraceDescription>
            </TraceLine>
            <TraceLine>
              <TraceIcon>
                <Icon as={MagnifyingGlassIcon} />
              </TraceIcon>
              <TraceTitle>Searched the code for cacheKey</TraceTitle>
              <TraceDescription>3 results</TraceDescription>
            </TraceLine>
          </Trace>
        </TraceCollapsibleContent>
      </TraceCollapsible>
      <Text>
        Image optimization reruns on every deploy because its cache key includes
        the commit hash. Key it on the lockfile instead.
      </Text>
    </View>
  )
}
```

```swift
import EsuissUI
import PhosphorSwift
import SwiftUI

struct ChainOfThoughtExample: 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 {
            TraceTitle(verbatim: "Researched in 3 steps")
          }
        }
        TraceCollapsibleContent {
          Trace {
            TraceLine {
              TraceIcon { Ph.fileText.regular }
              TraceTitle(verbatim: "Read the build log for dpl_8Kx2")
            }
            TraceLine {
              TraceIcon { Ph.globe.regular }
              TraceTitle(verbatim: "Searched the web for next.js image cache key")
              TraceDescription(verbatim: "2 sources")
            }
            TraceLine {
              TraceIcon { Ph.magnifyingGlass.regular }
              TraceTitle(verbatim: "Searched the code for cacheKey")
              TraceDescription(verbatim: "3 results")
            }
          }
        }
      }
      Text("Image optimization reruns on every deploy because its cache key includes the commit hash. Key it on the lockfile instead.").textStyle(.base).foregroundStyle(EsuissColor.foreground)
    }
  }
}
```

```kotlin
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.FileText
import com.adamglin.phosphoricons.regular.Globe
import com.adamglin.phosphoricons.regular.MagnifyingGlass
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
import com.esuiss.ui.components.Text
import com.esuiss.ui.components.Trace
import com.esuiss.ui.components.TraceCollapsible
import com.esuiss.ui.components.TraceCollapsibleContent
import com.esuiss.ui.components.TraceCollapsibleTrigger
import com.esuiss.ui.components.TraceDescription
import com.esuiss.ui.components.TraceIcon
import com.esuiss.ui.components.TraceLine
import com.esuiss.ui.components.TraceTitle

@Composable
fun ChainOfThoughtAIDefault() {
    Column(Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp)) {
        Message(Modifier.fillMaxWidth(), align = MessageAlign.End) {
            MessageContent {
                Bubble(variant = BubbleVariant.Muted) { BubbleContent("Why is the checkout deploy so slow?") }
            }
        }
        TraceCollapsible(initiallyExpanded = true) {
            TraceCollapsibleTrigger {
                TraceLine {
                    TraceTitle("Researched in 3 steps")
                }
            }
            TraceCollapsibleContent {
                Trace {
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.FileText, contentDescription = null) }
                        TraceTitle("Read the build log for dpl_8Kx2")
                    }
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.Globe, contentDescription = null) }
                        TraceTitle("Searched the web for next.js image cache key")
                        TraceDescription("2 sources")
                    }
                    TraceLine {
                        TraceIcon { Icon(PhosphorIcons.Regular.MagnifyingGlass, contentDescription = null) }
                        TraceTitle("Searched the code for cacheKey")
                        TraceDescription("3 results")
                    }
                }
            }
        }
        Text("Image optimization reruns on every deploy because its cache key includes the commit hash. Key it on the lockfile instead.")
    }
}
```

Chain of Thought on mobile is built from the same parts as on the web: [Trace](https://suiss.app/ui/docs/components/trace), [Message](https://suiss.app/ui/docs/components/message), [Bubble](https://suiss.app/ui/docs/components/bubble). The example above is a static demo; connect it to your agent by rendering the same parts from your messages.

## Installation

**React Native**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble
```

**SwiftUI**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble --platform swift
```

**Jetpack Compose**

```bash title="Terminal"
npx @esuiss/ui@latest add trace message bubble --platform kotlin
```

## API Reference

See [Trace](https://suiss.app/ui/docs/components/trace), [Message](https://suiss.app/ui/docs/components/message), [Bubble](https://suiss.app/ui/docs/components/bubble).

