# Action card

Package: @commenda/pathfinder@0.1.0
Page: /docs/components/action-card
Figma: https://www.figma.com/design/dn7BkGYEfIyP0o2zfesfVh/The-System?node-id=658-33976

```tsx
import { ActionCard } from '@commenda/pathfinder/action-card';

<ActionCard
  title="Connect your accounting software"
  description="Bring transactions into Commenda."
  artwork={<IntegrationLogo />}
  actionConfig={{
    label: 'Connect account',
    onAction: connectAccount,
    pendingDescription: 'Finish connecting in the window that opened.',
  }}
  dismissConfig={{ onDismiss: persistDismissal }}
/>;
```


## Usage [#usage]

```tsx
import { ActionCard } from '@commenda/pathfinder/action-card';

<ActionCard
  title="Connect your accounting software"
  description="Bring transactions into Commenda."
  artwork={<IntegrationLogo />}
  actionConfig={{
    label: 'Connect account',
    onAction: connectAccount,
    pendingDescription: 'Finish connecting in the window that opened.',
  }}
  dismissConfig={{ onDismiss: persistDismissal }}
/>;
```

## Inline action [#inline-action]


```tsx
import { ActionCard } from '@commenda/pathfinder/action-card';

<ActionCard
  placement="inline"
  title="Connect your accounting software"
  description="Bring transactions into Commenda."
  artwork={<IntegrationLogo />}
  actionConfig={{
    label: 'Connect account',
    onAction: connectAccount,
    pendingDescription: 'Finish connecting in the window that opened.',
  }}
  dismissConfig={{ onDismiss: persistDismissal }}
/>;
```


## Failure and retry [#failure-and-retry]


```tsx
// Return { status: 'failed', message: 'Please try again.' } from the handler to retain this card.
import { ActionCard } from '@commenda/pathfinder/action-card';

<ActionCard
  title="Connect your accounting software"
  description="Bring transactions into Commenda."
  artwork={<IntegrationLogo />}
  actionConfig={{
    label: 'Connect account',
    onAction: connectAccount,
    pendingDescription: 'Finish connecting in the window that opened.',
  }}
  dismissConfig={{ onDismiss: persistDismissal }}
/>;
```


Reset restores the example. Simulate failure retains the card for retry; no backend request is made.

## API reference [#api-reference]

| Prop | Type | Requirement | Behavior |
| --- | --- | --- | --- |
| `title` | `string` | required | Required title and optional supporting text |
| `description` | `string` | optional | Required title and optional supporting text |
| `artwork` | `ReactElement<any, string \| React.JSXElementConstructor<any>>` | required | Decorative artwork rendered in the existing 40px Avatar |
| `actionConfig` | `{ label: string; onAction: () => ActionResult \| Promise<ActionResult>; pendingDescription?: string; }` | required | Handler returns `{ status: 'completed' }` or `{ status: 'failed', message }`, synchronously or asynchronously |
| `dismissConfig` | `{ label?: string; onDismiss: () => void; }` | optional | Optional secondary action, defaults to Not now; persists permanent dismissal |
| `state` | `"ready" \| "pending" \| "completed" \| "dismissed"` | optional | Host status; local pending/completion also tracked |
| `placement` | `"docked" \| "inline"` | optional | Docked radius16/raised, inline radius10/flat |
| `className` | `string` | optional | Layout override |

### actionConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | Yes |  |
| `onAction` | `() => ActionResult \| Promise<ActionResult>` | Yes |  |
| `pendingDescription` | `string` | No |  |

### dismissConfig fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `label` | `string` | No |  |
| `onDismiss` | `() => void` | Yes |  |

## Design rules [#design-rules]

- **ACTION-01:** Optional next action outside the blocking request flow. The frame is not a click target; only buttons activate.
- **ACTION-02:** One primary action and at most one secondary dismissal. Loading retains the action label with a spinner. Explain external-window work in the pending description when relevant.
- **ACTION-03:** Completion or dismissal removes the card. The host persists that outcome. No automatic agent turn, success toast, or promised follow-up. Historical cards remain usable until acted on or dismissed.

Return completion only when the action succeeds, not merely when an external window opens. If a blocked popup fails, return a failure message. Use a new React key for a new action, and store its completion/dismissal independently from conversational requests.

## Integration and accessibility [#integration-and-accessibility]

Buttons have explicit names and keyboard focus. No network transport or AI SDK provider is required. Requests, tool execution, permission checks, durable outcomes and composer coordination stay in the application. See [AI content integration](/docs/guides/ai-content) for migration mappings; application migration is outside this build.
