Skip to content
Tier

Narrative block

Pro

Report prose as claims, each followed by what it rests on: a source as a Citation marker, a section of the report as a mark that opens it. A claim that states a figure and gives no evidence is never shown as plain fact: it is muted, underlined with a dashed line and followed by "No evidence given for this figure". A claim with no figure and no evidence reads as ordinary prose. The claims are model output, drawn as text.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2 and 4.1.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/narrative-block

Brings in: bidi, chat, citation, heading-level, heading-scope, locale, tokens, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import { NarrativeBlock } from "@/components/candor/narrative-block"

// The second claim states a figure with nothing behind it, so it is shown as unsupported rather than as fact.
export default function Example() {
  return (
    <NarrativeBlock
      className="max-w-xl"
      heading="Summary"
      onOpenSection={() => {}}
      claims={[
        {
          id: "growth",
          text: "Pipeline grew 12% against last quarter, mostly in the mid-market segment.",
          evidence: [{ id: "q1", marker: 1, kind: "query", label: "Pipeline by segment", origin: "Query on opportunities" }, { section: 2, title: "Pipeline" }],
        },
        { id: "win", text: "Win rate fell to 18%." },
        { id: "q3", text: "Q3 deals are concentrated in late stages.", numeric: false, evidence: [{ section: 3, title: "Stages" }] },
      ]}
    />
  )
}

<NarrativeBlock>

PropTypeDefaultDescription
claimsrequiredNarrativeClaim[]—The prose, as claims in reading order.
onOpenSection((number: number) => void) | undefined—Opens a section of the report by its number. Without it a section is named but isn't a button.
headingstring | undefined—A heading over the prose, "Summary".
headingLevelHeadingLevel | undefined—The heading's level in the page's outline. Defaults to one below the nearest `HeadingScope`, else 2 (`<h2>`).
classNamestring | undefined—Classes for the outer box.

Functions

claimStatesNumber(text: string)

Whether text holds a decimal digit in any script (3, ٣, ३): a claim that does states a figure.

Strings

The fixed words, in English by default. Change any of them under narrativeBlock in <LocaleProvider strings={{ … }}>; the table is exported as narrativeBlockStrings.

KeyEnglish
unsupportedNo evidence given for this figure
sectionMarker§{number}
sectionLabelSection {number}: {title}

Motion

MomentFamilyWhat it does
tinttintA section mark's border and text colour change when the pointer is over it, and a ring fades in when it takes keyboard focus.

Set with --cd-motion-narrative-block-<moment>-* in your brand file; the families are in the design reference.

Types

NarrativeSection
A section of the report a claim rests on, opened through `onOpenSection`.
{ /** The section's number, as `ReportSection` shows it. */ section: number; /** The section's title. */ title: string; }
NarrativeEvidence
What a claim rests on: a source (the free `Citation`'s shape, a document, a query, a web page) or a section of the report.
MessageSource | NarrativeSection
NarrativeClaim
One claim in the prose and what it rests on.
{ /** Stable across renders. */ id: string; /** The claim's words, from the model: drawn as text. */ text: string; /** The sources and sections the claim rests on. A claim that states a figure and has none is shown as unsupported. */ evidence?: NarrativeEvidence[] | undefined; /** * Whether the claim states a figure. Left out, a claim states one when its text holds a digit (in any script). Set it * `false` for digits that aren't a figure ("Q3", a year) and `true` for a figure in words ("twelve accounts"). */ numeric?: boolean | undefined; }

Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

HeadingLevel from @/lib/candor/heading-level
A heading's level in the page's outline: `1` is `<h1>`, `6` is `<h6>`. Every Candor item that draws a heading takes its `headingLevel` as this type; the level is where the heading sits in the outline, never how large it looks.
1 | 2 | 3 | 4 | 5 | 6
MessageSource from @/lib/candor/chat
{ id: string; /** The [n] shown inline. Assigned by the service, so it is stable across renders. */ marker: number; kind: SourceKind; label: string; /** An inert excerpt shown in the popover. Text only. */ snippet?: string | undefined; /** Where it came from in a human sense: "Docs · Sales process v3", "Query on orders". */ origin?: string | undefined; provisional?: boolean | undefined; }
SourceKind from @/lib/candor/chat
"document", "web", "query", "code", "data", "workflow", "knowledge", or the app's own; unknown kinds read as "Source".
string