Skip to content
Get started

Components

ResponseBlock

Unstable: one AI answer from the first wait to its end, thinking, streaming with a Stop button, complete with its actions, or an error with a retry. Import it from @nizivo/react/unstable/response-block.

Server component, no client JSUnstable

Examples

Basic

Four states of one answer: thinking, streaming with Stop, complete with actions, and an error with Retry. Pass onStop and onRetry from the client component that streams the answer.

AssistantAI generated
Reading last week's orders…
AssistantAI generated

Your three best sellers last week were the Moss mug, the Kiln

AssistantAI generated

Your three best sellers last week were the Moss mug, the Kiln bowl and the linen apron. The mug sold out on Thursday.

AssistantAI generated
import type { JSX } from 'react';
import { IconButton } from '@nizivo/react/icon-button';
import { ResponseBlock } from '@nizivo/react/unstable/response-block';

/** Stand-ins for `@nizivo/icons` glyphs (Lucide strokes). */
function Copy(): JSX.Element {
  return (
    <svg
      viewBox="0 0 24 24"
      width="1em"
      height="1em"
      fill="none"
      stroke="currentColor"
      strokeWidth={1.92}
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
      <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
    </svg>
  );
}

function Refresh(): JSX.Element {
  return (
    <svg
      viewBox="0 0 24 24"
      width="1em"
      height="1em"
      fill="none"
      stroke="currentColor"
      strokeWidth={1.92}
      strokeLinecap="round"
      strokeLinejoin="round"
    >
      <path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
      <path d="M21 3v5h-5" />
      <path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
      <path d="M8 16H3v5" />
    </svg>
  );
}

export function ResponseBlockExample(): JSX.Element {
  return (
    <div style={{ display: 'grid', gap: 24, maxInlineSize: 520 }}>
      <ResponseBlock status="thinking" thinking="Reading last week's orders…" />
      <ResponseBlock status="streaming">
        <p>Your three best sellers last week were the Moss mug, the Kiln</p>
      </ResponseBlock>
      <ResponseBlock
        status="complete"
        actions={
          <>
            <IconButton variant="tertiary" size="sm" label="Copy" icon={<Copy />} />
            <IconButton variant="tertiary" size="sm" label="Regenerate" icon={<Refresh />} />
          </>
        }
      >
        <p>
          Your three best sellers last week were the Moss mug, the Kiln bowl and the linen apron.
          The mug sold out on Thursday.
        </p>
      </ResponseBlock>
      <ResponseBlock
        status="error"
        error="The orders service did not answer. Nothing was changed."
      />
    </div>
  );
}

Import

import { ResponseBlock } from '@nizivo/react/unstable/response-block';

// Once per app (Getting started), after
// @nizivo/tokens/tokens.css and @nizivo/react/css/ai-badge.css and @nizivo/react/css/base.css and @nizivo/react/css/button.css and @nizivo/react/css/thinking-indicator.css
import '@nizivo/react/css/response-block.css';

Static or interactive

Server. No client JavaScript of its own: Stop and Retry are Buttons that call onStop and onRetry, so the block is rendered by the client component that streams the answer. How to choose.

States

status moves through thinking (a ThinkingIndicator, with the step in thinking), streaming (the text so far, a caret, and a Stop button that calls onStop), complete (the answer and actions, such as copy, regenerate and a FeedbackBar) and error (the error message, and a Retry button that calls onRetry).

Screen readers

The answer sits in a polite live region that is aria-busy while it streams, so it is read once, when complete, not word by word. The error is an alert. Stop is an ordinary button, always in the Tab order while the answer streams.

name ("Assistant" by default) names the block; avatar replaces the sparkles mark and badge the AiBadge (badge={null} hides it).

Props

Server component Renders data-slot="response-block" with the class nz-response-block. Props: ResponseBlockProps. Source: src/unstable/response-block/response-block.tsx.

NameTypeDefaultDescription
actionsReactNode–

Shown once the answer is complete, for example copy and regenerate buttons and a FeedbackBar.

avatarReactNode–

Replaces the sparkles mark, for example an Avatar of the assistant.

badgeReactNode<AiBadge />

Beside the name; null hides it.

errorReactNode–

The error message. Replaces labels.error.

labelsPartial<ResponseBlockLabels>–
nameReactNode–

Who answers. Replaces labels.name.

onRetry() => void–

The "Retry" Button, shown after an error, calls it.

onStop() => void–

The "Stop generating" Button, shown while streaming, calls it. Pass it from the client component that streams the answer.

renderRenderProp<Record<string, never>>–

Must render a <div>.

statusrequiredResponseStatus
"thinking""streaming""complete""error"
–
thinkingReactNode–

While thinking: the step, such as "Reading last week's orders…".

  • Also takes every attribute of <div> except title. (Omit<ComponentPropsWithRef<"div">, "title">)

render takes an element, or, in client code, a function of the props and no state.

Other exports

ResponseBlockLabels type

import type { ResponseBlockLabels } from '@nizivo/react/unstable/response-block'

ResponseBlock's own strings (§16.4): serialisable, English defaults.

NameTypeDefaultDescription
errorrequiredstring'Something went wrong. Try again.'

The error message, unless error is given.

namerequiredstring'Assistant'

Who answers, unless name is given.

retryrequiredstring'Retry'
stoprequiredstring'Stop generating'
ResponseBlockProps type

import type { ResponseBlockProps } from '@nizivo/react/unstable/response-block'

The props of ResponseBlock, listed above.

ResponseStatus type

import type { ResponseStatus } from '@nizivo/react/unstable/response-block'

Where the answer is: thinking, streaming, complete or failed.

type ResponseStatus = "thinking" | "streaming" | "complete" | "error"

Data attributes and slots

Style parts by class (.nz-<slot>) and state by data attribute. Attribute meanings come from contracts/vocabulary.json.

SlotRendered byData attributes
response-block
.nz-response-block
ResponseBlock
root (takes render)
  • data-tone: "thinking""streaming""complete""error"
    The tone prop; values in each component manifest
response-block-actions
.nz-response-block-actions
ResponseBlock
inner element (CSS only)
none
response-block-content
.nz-response-block-content
ResponseBlock
inner element (CSS only)
none
response-block-error
.nz-response-block-error
ResponseBlock
inner element (CSS only)
none
response-block-name
.nz-response-block-name
ResponseBlock
inner element (CSS only)
none

CSS hooks

Set a hook on the component's class or any ancestor to restyle it without specificity games; leave it unset for the default shown.

This stylesheet reads no public hook.

Layers: nz.components. Classes: .nz-response-block, .nz-response-block-actions, .nz-response-block-avatar, .nz-response-block-body, .nz-response-block-content, .nz-response-block-error, .nz-response-block-error-icon, .nz-response-block-error-text, .nz-response-block-header, .nz-response-block-name.

Tokens this stylesheet reads (20)

--nz-bg-brand, --nz-bg-critical-subtle, --nz-duration-pulse, --nz-font-body-m, --nz-font-body-s, --nz-font-label-m-strong, --nz-icon-critical, --nz-radius-full, --nz-radius-xs, --nz-size-icon-sm, --nz-space-0-5, --nz-space-1, --nz-space-1-5, --nz-space-2, --nz-space-3, --nz-space-6, --nz-space-8, --nz-text-critical, --nz-text-on-brand, --nz-text-primary

Accessibility

Nothing in this component takes focus or keyboard input.

Keyboard

None.

On this page