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.
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.
Your three best sellers last week were the Moss mug, the Kiln
Your three best sellers last week were the Moss mug, the Kiln bowl and the linen apron. The mug sold out on Thursday.
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.
Header
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.
| Name | Type | Default | Description |
|---|---|---|---|
actions | ReactNode | – | Shown once the answer is complete, for example copy and regenerate buttons and a FeedbackBar. |
avatar | ReactNode | – | Replaces the sparkles mark, for example an Avatar of the assistant. |
badge | ReactNode | <AiBadge /> | Beside the name; |
error | ReactNode | – | The error message. Replaces |
labels | Partial<ResponseBlockLabels> | – | |
name | ReactNode | – | Who answers. Replaces |
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. |
render | RenderProp<Record<string, never>> | – | Must render a |
statusrequired | ResponseStatus"thinking""streaming""complete""error" | – | |
thinking | ReactNode | – | While thinking: the step, such as "Reading last week's orders…". |
- Also takes every attribute of
<div>excepttitle. (Omit<ComponentPropsWithRef<"div">, "title">)
render takes an element, or, in client code, a function of the props and no state.
Other exports
ResponseBlockLabelstypeimport type { ResponseBlockLabels } from '@nizivo/react/unstable/response-block'ResponseBlock's own strings (§16.4): serialisable, English defaults.
Name Type Default Description errorrequiredstring'Something went wrong. Try again.'The error message, unless
erroris given.namerequiredstring'Assistant'Who answers, unless
nameis given.retryrequiredstring'Retry'stoprequiredstring'Stop generating'ResponseBlockPropstypeimport type { ResponseBlockProps } from '@nizivo/react/unstable/response-block'The props of
ResponseBlock, listed above.ResponseStatustypeimport 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.
| Slot | Rendered by | Data attributes |
|---|---|---|
response-block.nz-response-block | ResponseBlockroot (takes render) |
|
response-block-actions.nz-response-block-actions | ResponseBlockinner element (CSS only) | none |
response-block-content.nz-response-block-content | ResponseBlockinner element (CSS only) | none |
response-block-error.nz-response-block-error | ResponseBlockinner element (CSS only) | none |
response-block-name.nz-response-block-name | ResponseBlockinner 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.
Rating
Unstable: a read-only score, five stars with the score and the review count written out, read as one sentence. Import it from @nizivo/react/unstable/rating.
SearchField
A search or filter input in a <search> landmark, with a magnifier, a clear button and an optional shortcut hint. Inside a <form method="get"> it filters with no JavaScript; inside a Field it is labelled, described and validated by the Field. It is also the top bar's search, as a real input.