---
title: Cards
description: Rich card components for cross-platform interactive messages.
type: reference
---

Card components render natively on each platform — Block Kit on Slack, Adaptive Cards on Teams, Embeds on Discord, and Google Chat Cards.

```typescript
import { Card, Text, CardLink, Button, Actions, Section, Fields, Field, Divider, Image, LinkButton, Table } from "chat";
```

All components support both function-call and JSX syntax. Function-call syntax is recommended for better type inference.

## Card

Top-level container for a rich message.

```typescript
Card({
  title: "Order #1234",
  subtitle: "Pending approval",
  children: [Text("Total: $50.00")],
})
```

<TypeTable
  type={{
    title: {
      description: 'Card title.',
      type: 'string',
    },
    subtitle: {
      description: 'Card subtitle.',
      type: 'string',
    },
    imageUrl: {
      description: 'Header image URL.',
      type: 'string',
    },
    children: {
      description: 'Card content elements.',
      type: 'CardChild[]',
    },
  }}
/>

## Text

Text content element. Use `CardText` instead of `Text` in JSX to avoid conflicts with React's built-in types.

```typescript
Text("Hello, world!")
Text("Important", { style: "bold" })
Text("Subtle note", { style: "muted" })
```

<TypeTable
  type={{
    content: {
      description: 'Text content (first argument).',
      type: 'string',
    },
    'options.style': {
      description: 'Text style.',
      type: '"plain" | "bold" | "muted"',
    },
  }}
/>

## Button

Interactive button that triggers an `onAction` handler.

```typescript
Button({ id: "approve", label: "Approve", style: "primary" })
Button({ id: "delete", label: "Delete", style: "danger", value: "item-123" })
```

<TypeTable
  type={{
    id: {
      description: 'Unique action ID for callback routing.',
      type: 'string',
    },
    label: {
      description: 'Button label text.',
      type: 'string',
    },
    style: {
      description: 'Visual style.',
      type: '"primary" | "danger" | "default"',
    },
    value: {
      description: 'Optional payload sent with the action callback.',
      type: 'string',
    },
    actionType: {
      description: 'Hints to adapters like Teams that this button will open a modal via event.openModal().',
      type: '"action" | "modal"',
      default: '"action"',
    },
  }}
/>

## CardLink

Inline hyperlink rendered as text. Can be placed directly in a card alongside other content, unlike `LinkButton` which must live inside `Actions`.

```typescript
CardLink({ url: "https://example.com", label: "Visit Site" })
```

<TypeTable
  type={{
    url: {
      description: 'URL to link to.',
      type: 'string',
    },
    label: {
      description: 'Link label text.',
      type: 'string',
    },
  }}
/>

## LinkButton

Button that opens a URL. No `onAction` handler needed.

```typescript
LinkButton({ url: "https://example.com", label: "View Docs" })
```

<TypeTable
  type={{
    url: {
      description: 'URL to open when clicked.',
      type: 'string',
    },
    label: {
      description: 'Button label text.',
      type: 'string',
    },
    style: {
      description: 'Visual style.',
      type: '"primary" | "danger" | "default"',
    },
  }}
/>

## Actions

Container for buttons and interactive elements. Required wrapper around `Button`, `LinkButton`, `Select`, and `RadioSelect`.

```typescript
Actions([
  Button({ id: "approve", label: "Approve", style: "primary" }),
  Button({ id: "reject", label: "Reject", style: "danger" }),
  LinkButton({ url: "https://example.com", label: "View" }),
])
```

## Section

Groups related content together.

```typescript
Section([
  Text("Grouped content"),
  Image({ url: "https://example.com/photo.png" }),
])
```

## Fields

Renders key-value pairs in a compact, multi-column layout.

```typescript
Fields([
  Field({ label: "Name", value: "Jane Smith" }),
  Field({ label: "Role", value: "Engineer" }),
])
```

## Field

A single key-value pair. Must be used inside `Fields`.

<TypeTable
  type={{
    label: {
      description: 'Field label.',
      type: 'string',
    },
    value: {
      description: 'Field value.',
      type: 'string',
    },
  }}
/>

## Image

Embeds an image in the card.

```typescript
Image({ url: "https://example.com/screenshot.png", alt: "Screenshot" })
```

<TypeTable
  type={{
    url: {
      description: 'Image URL.',
      type: 'string',
    },
    alt: {
      description: 'Alt text for accessibility.',
      type: 'string',
    },
  }}
/>

## Table

Structured data display with column headers and rows.

```typescript
Table({
  headers: ["Name", "Age", "Role"],
  rows: [
    ["Alice", "30", "Engineer"],
    ["Bob", "25", "Designer"],
  ],
})
```

<TypeTable
  type={{
    headers: {
      description: 'Column header labels.',
      type: 'string[]',
    },
    rows: {
      description: 'Data rows (each row is an array of cell strings).',
      type: 'string[][]',
    },
    align: {
      description: 'Column alignment.',
      type: '"left" | "center" | "right"[]',
    },
  }}
/>

On platforms with native table support (Teams, GitHub, Linear), tables render as formatted tables. On other platforms (Slack, Google Chat, Discord, Telegram), tables render as padded ASCII text.

## Divider

A visual separator between sections.

```typescript
Divider()
```

## CardChild types

The `children` array in `Card` and `Section` accepts these element types:

| Type | Created by |
|------|-----------|
| `TextElement` | `Text()` |
| `LinkElement` | `CardLink()` |
| `ImageElement` | `Image()` |
| `DividerElement` | `Divider()` |
| `ActionsElement` | `Actions()` |
| `SectionElement` | `Section()` |
| `FieldsElement` | `Fields()` |
| `TableElement` | `Table()` |
