> ## Documentation Index
> Fetch the complete documentation index at: https://docs.puntego.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React components

> Build the answer card and the ask bar with your own design system: @puntego/react/ui, the typed turn model, versioned events and host read tools.

The built-in guide is a calm card: the question, a one-sentence verdict, one
highlighted fact, follow-up chips and a footer with a clearly labelled next step.
`@puntego/react/ui` gives you that card as data and as replaceable components, so
you can render it with your own buttons, chips and typography while Puntego keeps
consent, confirmation, plan limits and action policy.

> **Release preview:** `@puntego/react` (including `@puntego/react/ui`) is
> implemented and tested in the repository but not yet published to npm. The
> hosted script works today; see [Install](/install).

`@puntego/react/ui` is a separate entry point: apps that do not import it ship
none of it.

<h2 id="card">
  The card with your components
</h2>

Mount the provider in headless mode, then render the card and the ask bar. Any
piece you do not replace is unstyled semantic HTML.

```tsx theme={null}
'use client';

import { PuntegoProvider } from '@puntego/react';
import { PuntegoAnswerCard, PuntegoAskBar, type PuntegoAnswerCardComponents } from '@puntego/react/ui';
import { Button, Chip, Pill, Stat } from '@/design-system';

const components: Partial<PuntegoAnswerCardComponents> = {
  Question: ({ children }) => <Pill>{children}</Pill>,
  Verdict: ({ children }) => <p className="text-base">{children}</p>,
  Fact: ({ lines, title }) => <Stat label={title} rows={lines} />,
  Chip: ({ children, onClick }) => <Chip onClick={onClick}>{children}</Chip>,
  Action: ({ children, disabled, onClick, variant }) => (
    <Button disabled={disabled} onClick={onClick} variant={variant === 'primary' ? 'solid' : 'ghost'}>
      {children}
    </Button>
  ),
};

export function Guide() {
  return (
    <PuntegoProvider appId="gp_your_app_id" headless>
      <aside aria-label="Product guide">
        <PuntegoAnswerCard components={components} />
        <PuntegoAskBar />
      </aside>
    </PuntegoProvider>
  );
}
```

| Piece | Props your component receives |
| - | - |
| `Root` | `children`, `aria-label`, `data-puntego-phase` (spread them on your element) |
| `Question` | `children`: the visitor's question |
| `Answer` | `children`, `aria-live="polite"`, `aria-busy`: the live region; keep both attributes |
| `Status` | `children`: "Looking through this page…", "Looking up your account…" or "Still preparing a response…" while the guide works, before its first words |
| `Verdict` | `children`: the answer's first sentence (or the error of a failed turn) |
| `Fact` | the highlighted fact: `title` (its heading, or `null`), `lines` (its rows, heading not repeated), `children` (the whole text), `locale` (the card's language) and `block` |
| `Detail` | `children`: the rest of the answer, and the "Stopped" or "withheld" note when it applies |
| `Block` | the answer's other blocks (steps, a table, a callout, a chart…), with the same props as `Fact` |
| `Chips` / `Chip` | the group (`aria-label`, `role`) / one chip: `children`, `onClick`, `followUp` |
| `Actions` / `Action` | the footer row / one action: `children` (its localized label), `onClick`, `disabled`, `variant` (`primary`, `neutral`, `tertiary`), `action` |
| `Sources` | `sources`, `label` |
| `Meta` | `aiLabel`, `poweredBy`: the AI identification and, when your plan requires it, the attribution |

Screen readers hear the status line from a visually hidden `role="status"`
element the card keeps inside `Root` (it takes no space in your layout); the
answer region is announced once the answer settles. For `PuntegoAskBar`, pass the
same component as `Submit` and `Stop` (it receives `onClick` only as Stop) so
one button switches in place and keyboard focus stays on it, as in the built-in
ask bar.

Keep action labels as text: the card's actions are labelled buttons, never icons
alone. The chips, the action labels, the AI label and error messages arrive
localized in the conversation's language; the few words the components write
themselves (the ask bar's placeholder and buttons, the status line) follow it too,
in English, Spanish and Portuguese.

`PuntegoAnswerCard` takes `onEditQuestion(question)` to put the question back in
your own input for the footer's **Edit question**, and `turn` to render a
specific turn instead of the latest.

<h2 id="charts">
  Charts and KPI tiles
</h2>

An answer's [chart](/generative-ui#charts) arrives with its layout already
worked out: `block.chart` holds the categories, each series' values (the
visitor-facing `display`, the `value`, and a `position` from 0 at the bottom to
1 at the top of the plot, or a donut part's share), the zero line
(`baseline`), the gridlines (`ticks`) and the `format` the figures read in. A
metric grid's `block.metrics` holds each tile's `value`, `limit`, `meter` (how
full, 0 to 1), `delta` (`direction`, signed `display`, `sentiment`), `change`
and `trend`. Your chart shows exactly the scale and shares the built-in card
shows.

The default `Block` draws a chart with `PuntegoChart` and the default `Fact`
draws KPI tiles with `PuntegoMetricTiles`; both are unstyled SVG and HTML that
use `--gp-accent-strong` and `--gp-chart-2` … `--gp-chart-5` from
[design tokens](/design-tokens) when you set them. To draw with your own chart
library, take the words from `chartParts`:

```tsx theme={null}
import { chartParts, PuntegoChart } from '@puntego/react/ui';

const components: Partial<PuntegoAnswerCardComponents> = {
  Block: ({ block, lines, locale, title }) => {
    if (!block.chart) return <Rows title={title} rows={lines} />;
    const { points, summary, table, ticks } = chartParts(block.chart, { locale });
    return <MyChart chart={block.chart} points={points} summary={summary} table={table} ticks={ticks} />;
  },
};
```

`chartParts` gives the one-line `summary` to read with the chart
(`aria-describedby`), every point's accessible `label` ("Growth: \$99.00"), the
gridline labels in the chart's format and the same figures as a `table`, in
English, Spanish or Portuguese. Keep what the built-in chart keeps: one tab
stop with the arrow keys walking the points, a legend for two series, the
table for anyone who prefers it, and no motion for visitors who ask for less.

<h2 id="data">
  Build it from data
</h2>

For full control, read the parts and render them yourself:

```tsx theme={null}
import { answerBlockLines, usePuntegoAnswerCard } from '@puntego/react/ui';

function MyCard() {
  const { parts, runAction, selectChip } = usePuntegoAnswerCard();
  if (!parts) return null;
  return (
    <article aria-label="Answer">
      {parts.question && <Pill>{parts.question}</Pill>}
      <div aria-live="polite" aria-busy={parts.busy}>
        {parts.status && <p>{parts.status}</p>}
        <p>{parts.verdict}</p>
        {parts.fact && <Stat {...answerBlockLines(parts.fact)} />}
        {parts.detail && <p>{parts.detail}</p>}
        {parts.moreBlocks.map((block) => (
          <Rows key={block.id} {...answerBlockLines(block)} />
        ))}
      </div>
      {/* Announce the working status outside the busy region. */}
      <p role="status" className="sr-only">{parts.status ?? ''}</p>
      {parts.chips.map((chip) => (
        <Chip key={chip.id} onClick={() => selectChip(chip.id)}>{chip.label}</Chip>
      ))}
      {parts.actions.map((action) => (
        <Button key={action.id} disabled={action.disabled} onClick={() => runAction(action.id)}>
          {action.label}
        </Button>
      ))}
      <small>{[parts.aiLabel, parts.poweredBy].filter(Boolean).join(' · ')}</small>
    </article>
  );
}
```

`answerCardParts(turn)` is the same projection as a pure function (safe during
server rendering). `runAction` goes through `runTurnAction`: it runs only an
action the latest turn offers right now, through the built-in button's own path,
and a write's **Do it** needs a real visitor gesture — call it from the click
handler itself.

<h2 id="events">
  Events for your analytics
</h2>

`usePuntegoEvents` subscribes to the guide's public events at once and delivers
each as a versioned envelope. The contract is version `1`: within it, event names
and payload fields are stable (new events and new optional fields may be added).

```tsx theme={null}
import { usePuntegoEvents } from '@puntego/react';

usePuntegoEvents((event) => {
  analytics.track(`guide ${event.name}`, { version: event.version });
}, { events: ['turn:complete', 'feedback:submitted', 'tool:result'] });
```

See the [event map](/sdk-reference#events) for every event and payload.

<h2 id="tools">
  Read your app's data
</h2>

`usePuntegoTool` lets the guide read the visitor's own data through a read-only
function of your app while the component is mounted. Only the listed `fields`
leave the page. See [Host read tools](/host-tools).

<h2 id="identity">
  Sign visitors in and out
</h2>

Pass a fresh one-time token to `PuntegoProvider` as `customerIdentityToken` on
login and account switches, and clear it on logout; outside React, call
`window.Puntego.setIdentity(token)` and `clearIdentity()`. See
[Frameworks](/frameworks#react).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.