---
title: Actions
description: Handle button clicks and interactive card events across platforms.
type: guide
prerequisites:
  - /docs/cards
related:
  - /docs/modals
---

Actions let you handle button clicks, dropdown selections, and other interactive events from [cards](/docs/cards). Register handlers with `onAction` to respond when users interact with your cards.

## Handle a specific action

```typescript title="lib/bot.ts" lineNumbers
bot.onAction("approve", async (event) => {
  await event.thread.post(`Order approved by ${event.user.fullName}!`);
});
```

## Handle multiple actions

```typescript title="lib/bot.ts" lineNumbers
bot.onAction(["approve", "reject"], async (event) => {
  const action = event.actionId === "approve" ? "approved" : "rejected";
  await event.thread.post(`Order ${action} by ${event.user.fullName}`);
});
```

## Catch-all handler

Register a handler without an action ID to catch all actions:

```typescript title="lib/bot.ts" lineNumbers
bot.onAction(async (event) => {
  console.log(`Action: ${event.actionId}, Value: ${event.value}`);
});
```

## ActionEvent

The `event` object passed to action handlers:

| Property | Type | Description |
|----------|------|-------------|
| `actionId` | `string` | The `id` from the Button or Select component |
| `value` | `string` (optional) | The `value` from the Button or selected option |
| `user` | `Author` | The user who clicked |
| `thread` | `Thread \| null` | The thread containing the card (null for view-based actions like home tab buttons) |
| `messageId` | `string` | The message containing the card |
| `threadId` | `string` | Thread ID |
| `adapter` | `Adapter` | The platform adapter |
| `triggerId` | `string` (optional) | Platform trigger ID (used for opening modals) |
| `openModal` | `(modal) => Promise<void>` | Open a modal dialog |
| `raw` | `unknown` | Platform-specific event payload |

## Pass data with buttons

Use the `value` prop on buttons to pass extra context to your handler:

```tsx title="lib/bot.tsx"
<Button id="report" value="bug">Report Bug</Button>
<Button id="report" value="feature">Request Feature</Button>
```

```typescript title="lib/bot.ts" lineNumbers
bot.onAction("report", async (event) => {
  if (event.value === "bug") {
    // Open bug report flow
  } else if (event.value === "feature") {
    // Open feature request flow
  }
});
```

## Open a modal from an action

Use `event.openModal()` to open a [modal](/docs/modals) in response to a button click:

```tsx title="lib/bot.tsx" lineNumbers
import { Modal, TextInput, Select, SelectOption } from "chat";

bot.onAction("feedback", async (event) => {
  await event.openModal(
    <Modal callbackId="feedback_form" title="Send Feedback" submitLabel="Send">
      <TextInput id="message" label="Your Feedback" multiline />
      <Select id="category" label="Category">
        <SelectOption label="Bug" value="bug" />
        <SelectOption label="Feature" value="feature" />
      </Select>
    </Modal>
  );
});
```

<Callout type="info">
Modals are currently supported on Slack. Other platforms will receive a no-op or fallback behavior.
</Callout>
