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

# React Components

> Render AI action results with custom React components in the default AI Chat Widget.

Use this page when you are using `@opencx/widget-react` and want specific AI action results to render as custom React UI.

<Tip>
  Need the broader concept first? Start with **[Custom Components](/widget/custom-components)**. Want to try widget options live without installing anything? Open the **[Playground](/widget/playground)**.
</Tip>

## How React Component Mapping Works

For the default React widget, you register an array of components on <Tooltip tip="The default React widget component — renders the full chat UI including launcher, message thread, and input.">`<Widget />`</Tooltip>.

Each entry needs:

* a `key`
* a React `component`

The `key` must match the action <Tooltip tip="The unique identifier for an AI action, defined when you create the action in the dashboard or API. Used in camelCase as the component key.">operation\_id</Tooltip> in camelCase.

## Step 1: Build The Component

Use <Tooltip tip="A typed React prop interface that provides the AI action result data to your custom component.">`WidgetComponentProps<T>`</Tooltip> so your component is typed to the action result you expect.

```tsx AccountBalanceCard.tsx theme={"dark"}
import { WidgetComponentProps } from '@opencx/widget-react-headless';

type AccountBalancePayload = {
  balance: number;
  currency: string;
};

export function AccountBalanceCard({
  data,
}: WidgetComponentProps<AccountBalancePayload>) {
  const result = data?.action?.data;

  if (!result) {
    return <div>Balance details are not available right now.</div>;
  }

  return (
    <div>
      <strong>Available balance</strong>
      <div>
        {result.balance} {result.currency}
      </div>
    </div>
  );
}
```

## Step 2: Register It On The Widget

```tsx WidgetWrapper.tsx theme={"dark"}
import { Widget } from '@opencx/widget-react';
import { AccountBalanceCard } from './AccountBalanceCard';

export function WidgetWrapper() {
  return (
    <Widget
      options={{
        token: '<WIDGET_TOKEN>',
      }}
      components={[
        {
          key: 'getAccountBalance',
          component: AccountBalanceCard,
        },
      ]}
    />
  );
}
```

## What To Watch For

<AccordionGroup>
  <Accordion title="Key mismatch" icon="key">
    If the component key does not match the action `operation_id`, the widget will not use your custom component.
  </Accordion>

  <Accordion title="Unexpected payload shape" icon="triangle-exclamation">
    Always render a fallback when `data?.action?.data` is missing or incomplete.
  </Accordion>

  <Accordion title="Keep the component narrow" icon="sliders">
    Focus each component on one action result. If a component tries to handle many unrelated actions, it becomes hard to trust and maintain.
  </Accordion>
</AccordionGroup>

## Good React Use Cases

<CardGroup cols={2}>
  <Card title="Account and order summaries" icon="rectangle-list">
    Show balances, plans, shipments, invoices, or usage totals as structured UI.
  </Card>

  <Card title="Status and confirmation cards" icon="circle-check">
    Show action outcomes like refunds, appointment bookings, or case updates in a clearer format.
  </Card>

  <Card title="Mini workflows" icon="sliders">
    Collect a few related inputs or guide the visitor through the next step after a successful action.
  </Card>

  <Card title="Rich fallback states" icon="wrench">
    Give visitors a clear next step when the action returns partial data or nothing useful.
  </Card>
</CardGroup>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Custom Components" icon="paintbrush" href="/widget/custom-components">
    Decide when custom UI is worth the added complexity.
  </Card>

  <Card title="Headless" icon="code" href="/widget/custom-components-headless">
    Build fully custom rendering when the default widget is not enough.
  </Card>

  <Card title="Configuration" icon="gear" href="/widget/configuration">
    Use widget behavior and mode configuration alongside custom UI.
  </Card>

  <Card title="Playground" icon="play" href="/widget/playground">
    Try widget options in a live form and copy the generated snippet.
  </Card>
</CardGroup>
