Reasoning

While the model works, the trigger names the current step, counts the seconds, and opens the trace. When it finishes, the label settles into a short summary like Worked for 6s, and the trail stays one tap away.

  1. Compared the incident with recent deployments
    The errors start two minutes after the last production deploy.
  2. Filtered payment errors by issuer country
    Only non-US cards fail, so the regression sits in address verification.
  3. Matching the first failing request
    The first failure lines up with the providerโ€™s new verification rule.
<script lang="ts">
    import * as Reasoning from '@sivir-ui/svelte/components/reasoning';
</script>

<Reasoning.Root duration={6} open class="w-full max-w-xl">
    <Reasoning.Trigger status="Matching the first failing request" />
    <Reasoning.Content>
        <Reasoning.Steps>
            <Reasoning.Step title="Compared the incident with recent deployments">
                The errors start two minutes after the last production deploy.
            </Reasoning.Step>
            <Reasoning.Step title="Filtered payment errors by issuer country">
                Only non-US cards fail, so the regression sits in address verification.
            </Reasoning.Step>
            <Reasoning.Step title="Matching the first failing request">
                The first failure lines up with the providerโ€™s new verification rule.
            </Reasoning.Step>
        </Reasoning.Steps>
    </Reasoning.Content>
</Reasoning.Root>

Installation

Copy the command below and run it in your terminal.

bunx @sivir-ui/svelte add reasoning

Usage

Live reasoning

Set streaming on Root while the model works. The trace opens and the elapsed seconds count up. Pass the current activity as status on Trigger; it defaults to Thinking. When streaming turns off, the trace stays as it is and the trigger reads Worked for and the elapsed time, or your summary. Bind open to control visibility yourself.

import * as Reasoning from '@sivir-ui/svelte/components/reasoning';

<Reasoning.Root {streaming}>
  <Reasoning.Trigger status="Reading the release history" />
  <Reasoning.Content>
    <p>Compared the incident timestamp with the last five deployments.</p>
  </Reasoning.Content>
</Reasoning.Root>

Steps

Compose Steps and Step inside Content for a trail joined by a rail. An active step shimmers its title, an icon snippet replaces the dot, and collapsible turns the title into a toggle for the body.

<Reasoning.Content>
  <Reasoning.Steps>
    <Reasoning.Step title="Searched 3 websites" collapsible>
      {#snippet icon()}<Globe />{/snippet}
      <!-- Source chips -->
    </Reasoning.Step>
    <Reasoning.Step title="Planning the layout" status="active">
      Name each step as it happens.
    </Reasoning.Step>
  </Reasoning.Steps>
</Reasoning.Content>

Trigger icon

The trigger shows no icon by default. Pass an icon snippet to render one before the label. It receives the trigger state, so Orb can fill while streaming is on, or you can render any 14px icon.

<Reasoning.Trigger {status}>
  {#snippet icon({ streaming })}
    <Reasoning.Orb active={streaming} />
  {/snippet}
</Reasoning.Trigger>

Restored transcripts

The trigger measures elapsed time while streaming is on. For a saved conversation, pass the recorded duration in seconds on Root instead.

<Reasoning.Root duration={42}>
  <Reasoning.Trigger />
  <Reasoning.Content>โ€ฆ</Reasoning.Content>
</Reasoning.Root>

Examples

Live reasoning

  1. Reading the release history
<script lang="ts">
    import { Button } from '@sivir-ui/svelte/components/button';
    import * as Reasoning from '@sivir-ui/svelte/components/reasoning';
    import { ResponseStream } from '@sivir-ui/svelte/components/response-stream';

    const steps = [
        {
            title: 'Reading the release history',
            body: 'Release web-2418 shipped at 13:58 UTC, two minutes before the first error.'
        },
        {
            title: 'Sampling current errors',
            body: 'Every failing request times out in the address-verification call.'
        },
        {
            title: 'Checking the provider status',
            body: 'The provider reports no incident, so the new synchronous call is the likely cause.'
        }
    ];

    let run = $state(0);
    let shown = $state(1);
    let done = $state(0);
    let streaming = $state(true);

    const status = $derived(streaming ? steps[shown - 1].title : undefined);

    function next() {
        done += 1;

        if (done < steps.length) {
            shown += 1;

            return;
        }

        streaming = false;
    }

    function replay() {
        shown = 1;
        done = 0;
        streaming = true;
        run += 1;
    }
</script>

<div class="flex w-full max-w-xl flex-col items-start gap-4">
    <Reasoning.Root {streaming}>
        <Reasoning.Trigger {status} />
        <Reasoning.Content>
            {#key run}
                <Reasoning.Steps>
                    {#each steps.slice(0, shown) as step, index (step.title)}
                        <Reasoning.Step
                            title={step.title}
                            status={index < done ? 'complete' : 'active'}
                        >
                            <ResponseStream
                                textStream={step.body}
                                speed={8}
                                class="text-sm font-normal text-foreground-muted"
                                onComplete={next}
                            />
                        </Reasoning.Step>
                    {/each}
                </Reasoning.Steps>
            {/key}
        </Reasoning.Content>
    </Reasoning.Root>
    <Button variant="ghost" disabled={streaming} onclick={replay}>Replay</Button>
</div>

Searched sources

  1. Checking response-time limits

    People notice a delay after about a second, so several seconds of reasoning needs feedback that changes.

<script lang="ts">
    import Globe from '@lucide/svelte/icons/globe';
    import Lightbulb from '@lucide/svelte/icons/lightbulb';
    import * as Reasoning from '@sivir-ui/svelte/components/reasoning';
    import * as Source from '@sivir-ui/svelte/components/source';

    const sources = [
        {
            domain: 'nngroup.com',
            href: 'https://www.nngroup.com/articles/response-times-3-important-limits/',
            icon: '/favicons/nngroup.com.png'
        },
        {
            domain: 'developer.mozilla.org',
            href: 'https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame',
            icon: '/favicons/developer.mozilla.org.png'
        },
        {
            domain: 'web.dev',
            href: 'https://web.dev/articles/optimize-inp',
            icon: '/favicons/web.dev.png'
        }
    ];
</script>

<Reasoning.Root duration={8} open class="w-full max-w-xl">
    <Reasoning.Trigger />
    <Reasoning.Content>
        <Reasoning.Steps>
            <Reasoning.Step title="Searched 3 websites" collapsible>
                {#snippet icon()}
                    <Globe />
                {/snippet}
                <div class="flex flex-wrap gap-1.5 pt-0.5">
                    {#each sources as source (source.href)}
                        <Source.Root href={source.href}>
                            <Source.Icon src={source.icon} fallback={source.domain} />
                            <Source.Label>{source.domain}</Source.Label>
                        </Source.Root>
                    {/each}
                </div>
            </Reasoning.Step>
            <Reasoning.Step title="Checking response-time limits">
                {#snippet icon()}
                    <Lightbulb />
                {/snippet}
                <p class="m-0 text-foreground-muted">
                    People notice a delay after about a second, so several seconds of reasoning
                    needs feedback that changes.
                </p>
            </Reasoning.Step>
        </Reasoning.Steps>
    </Reasoning.Content>
</Reasoning.Root>

Anatomy

Reasoning.Root - Holds the open and streaming state and measures elapsed time.

Reasoning.Trigger - Shows an optional icon, the live status, and timer, then the settled summary.

Reasoning.Orb - A liquid orb for the trigger icon that waves while active.

Reasoning.Content - Holds the reasoning trace.

Reasoning.Steps - Lists the steps of the trace.

Reasoning.Step - Shows one step with its icon, title, and body on a connecting rail.