Back to Copilotkit

Button

showcase/shell-docs/src/content/reference/bot/components/Button.mdx

1.60.13.6 KB
Original Source

Overview

Button is a clickable control for an Actions row. Its onClick handler is written inline in your JSX and bound by the engine: the button that reaches the platform carries only an opaque action id, and the click is routed back to your handler. Button is generic over its value prop, so the value the click carries is fully typed.

Import

tsx
import { Button } from "@copilotkit/bot-ui";

Props

<PropertyReference name="onClick" type="ClickHandler<TValue>"> Inline handler run when the button is clicked. Receives an [`InteractionContext<TValue>`](/reference/bot/types/InteractionContext). Must return `void` or `Promise<void>` — for a one-liner that ends in a value-returning call, use a block body: `onClick={async ({ thread }) => { await thread.post("done"); }}`. </PropertyReference> <PropertyReference name="value" type="TValue"> Value echoed back on click via `ctx.action.value`, and the value [`thread.awaitChoice`](/reference/bot/classes/Thread) resolves to. Drives the `TValue` type parameter — `ctx.action.value` is inferred with no cast. Serialized into the platform payload (up to 2000 chars on Slack), so don't put secrets in it. </PropertyReference> <PropertyReference name="style" type='"primary" | "danger"'> Visual accent. Omit for the default neutral style. </PropertyReference> <PropertyReference name="children" type="BotChildren"> The button label. </PropertyReference>

Usage

Inline reply

tsx
<Actions>
  <Button
    style="primary"
    onClick={async ({ thread }) => {
      await thread.post("🚀 Shipping!");
    }}
  >
    Ship it
  </Button>
</Actions>

Typed value with awaitChoice (human-in-the-loop)

The clicked button's value is what thread.awaitChoice resolves to:

tsx
function ConfirmWrite({ action }: { action: string }) {
  return (
    <Message>
      <Section>{action}</Section>
      <Actions>
        <Button style="primary" value={{ confirmed: true }}>Create</Button>
        <Button style="danger" value={{ confirmed: false }}>Cancel</Button>
      </Actions>
    </Message>
  );
}

const choice = await thread.awaitChoice<{ confirmed: boolean }>(
  <ConfirmWrite action="Create Linear issue CPK-1234?" />,
);

Handlers that close over data

Inline handlers are re-derived from the component's props after a restart. If a handler closes over data that can't be reconstructed from props, wrap it with bind().

Behavior

  • Content-stable binding — the handler is snapshotted under a minted opaque id (ck:…). Only that id and the button's value cross the wire to the platform; handler code and other props never leave the process.
  • Expiry — with the default in-memory ActionStore, clicks on buttons posted before a process restart are acked but ignored (no error message is posted).

On Slack

Renders as a button element: label truncated at 75 characters (SLACK_LIMITS.buttonText), action_id capped at 255, serialized value capped at 2000 characters (SLACK_LIMITS.buttonValue). Clicks are acked within Slack's 3-second deadline, then dispatched asynchronously.