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

# Card

> Group related information inside a bordered surface.

export const StoryEmbed = ({storyId, title, height = 120}) => <>
    <Tabs>
      <Tab title="Light">
        <iframe title={`${title} (light)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:light`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
      <Tab title="Dark">
        <iframe title={`${title} (dark)`} src={`https://storybook.twenty.com/iframe.html?id=${storyId}&viewMode=story&globals=colorScheme:dark`} width="100%" height={height} loading="lazy" allow="clipboard-write" style={{
  border: 0
}} />
      </Tab>
    </Tabs>
    <a href={`https://storybook.twenty.com/?path=/story/${storyId}`}>
      Open in Storybook
    </a>
  </>;

`Card` groups four presentational parts under one named export. Compose `Card.Root`, `Card.Header`, `Card.Content`, and `Card.Footer` to separate a title, body, and supporting actions. The parts do not manage selection, focus, or application state. The application owns the behavior of any controls placed inside them.

<StoryEmbed storyId="ui-surfaces-card--display" title="Card example" height={200} />

## Usage

```tsx theme={null}
import { useId } from 'react';
import { Button } from 'twenty-ui/primitives/input';
import { Card } from 'twenty-ui/primitives/surfaces';
import { Heading, Text } from 'twenty-ui/primitives/typography';

export const ImportSummary = () => {
  const titleId = useId();

  return (
    <Card.Root fullWidth role="region" aria-labelledby={titleId}>
      <Card.Header>
        <Heading id={titleId} level={2}>
          Import summary
        </Heading>
      </Card.Header>
      <Card.Content>
        <Text>24 records are ready to import.</Text>
      </Card.Content>
      <Card.Footer>
        <Button>Review records</Button>
      </Card.Footer>
    </Card.Root>
  );
};
```

Use [Heading](/ui/primitives/typography/heading) at the level required by the surrounding page. Put actions in [Button](/ui/primitives/input/button) or a native link so keyboard and assistive-technology users can activate them.

## Anatomy

```text theme={null}
Card.Root (outer surface)
├── Card.Header (optional)
├── Card.Content (optional, repeatable)
└── Card.Footer (optional)
```

Import `Card` from `twenty-ui/primitives/surfaces`. `Card.Root` supplies the outer border and clipping, but creates no sections internally. No part requires another part or a particular section order. The sections also work independently, without `Card.Root` or context.

Each part renders a `div` by default and accepts native div attributes, event handlers, `className`, `style`, and a DOM `ref`. Use `render` with an element or callback to choose a semantic target. No part infers an element or role from `onClick`.

`CardRootProps`, `CardHeaderProps`, `CardContentProps`, and `CardFooterProps` are exported from `twenty-ui/primitives/surfaces`, `twenty-ui/primitives`, and the root package.

## Appearance and dividers

`Card.Root` has a transparent background by default. `fullWidth` fills its container, and `backgroundColor` changes the outer background. Child sections have their own backgrounds.

`Card.Header` has a primary background, padding, and a bottom border. `Card.Content` has a secondary background and padding; its optional `divider` adds a bottom border. `Card.Footer` has a primary background, padding, and a top border unless `divider={false}`. Avoid adding two borders at the same section boundary.

```tsx theme={null}
import { Card } from 'twenty-ui/primitives/surfaces';
import { Text } from 'twenty-ui/primitives/typography';

export const ImportSections = () => (
  <Card.Root>
    <Card.Content divider>
      <Text>24 records are ready to import.</Text>
    </Card.Content>
    <Card.Content divider>
      <Text>3 records need review.</Text>
    </Card.Content>
    <Card.Footer divider={false}>
      <Text>Last checked just now.</Text>
    </Card.Footer>
  </Card.Root>
);
```

## Interactive composition

Choose a native button for an action or an anchor for navigation through `render`. Supply target-specific attributes such as `type`, `disabled`, `href`, and `target` on that element. The actual owner supplies its keyboard, focus, disabled, and link behavior. Card does not add those behaviors to a display `div`.

```tsx theme={null}
import { Button } from 'twenty-ui/primitives/input';
import { Card } from 'twenty-ui/primitives/surfaces';
import { Text } from 'twenty-ui/primitives/typography';

type ReviewCardProps = {
  disabled?: boolean;
  onReview: () => void;
  onDismiss: () => void;
};

export const ReviewCard = ({
  disabled,
  onReview,
  onDismiss,
}: ReviewCardProps) => (
  <Card.Root>
    <Card.Content
      render={<button type="button" disabled={disabled} />}
      onClick={onReview}
    >
      <Text render={<span />}>Review 24 ready records</Text>
    </Card.Content>
    <Card.Footer>
      <Button onClick={onDismiss}>Dismiss</Button>
    </Card.Footer>
  </Card.Root>
);
```

The action and dismiss button are siblings inside a display card. Do not place another button, link, input, or other interactive control inside a button or link owner. When a button contains Card parts, render those parts as phrasing elements such as `span`.

```tsx theme={null}
import { Card } from 'twenty-ui/primitives/surfaces';
import { Text } from 'twenty-ui/primitives/typography';

export const ImportLinkCard = () => (
  <Card.Root render={<a href="/imports" />} fullWidth>
    <Card.Content>
      <Text>View imports</Text>
    </Card.Content>
  </Card.Root>
);
```

Native button and link owners retain the card layout and get a visible focus outline. Explicit button and link owners highlight on hover. Disabled owners keep their disabled appearance. Custom owners must supply their interaction behavior, including keyboard activation and disabled handling when applicable.

## Independent parts and custom wrappers

A section can be used without the outer surface. Wrappers can use its exported props type and spread it to preserve native attributes, event handlers, refs, render composition, classes, and styles. With a custom target, put its target-specific ref on the supplied element. Base UI merges that ref with the part ref and composed props.

```tsx theme={null}
import { Card, type CardContentProps } from 'twenty-ui/primitives/surfaces';
import { Text } from 'twenty-ui/primitives/typography';

type SummaryContentProps = CardContentProps;

const SummaryContent = ({ children, ...props }: SummaryContentProps) => (
  <Card.Content {...props}>{children}</Card.Content>
);

export const StandaloneSummary = () => (
  <SummaryContent aria-label="Import summary" style={{ maxWidth: 360 }}>
    <Text>24 records are ready to import.</Text>
  </SummaryContent>
);
```

## Migration

Replace the four separate imports with `import { Card } from 'twenty-ui/primitives/surfaces'`. The root package and `twenty-ui/primitives` also export the same `Card` object.

| Previous API | Compound API |
| - | - |
| `Card` as a component | `Card.Root` |
| `CardHeader` | `Card.Header` |
| `CardContent` | `Card.Content` |
| `CardFooter` | `Card.Footer` |
| `ComponentProps<typeof Card>` | `ComponentProps<typeof Card.Root>` |
| `styled(Card)` | `styled(Card.Root)` |

Apply the same member names to JSX tags, styled wrappers, and prop types. Standalone part exports are removed without compatibility aliases. Layout props, styling defaults, and independent section use remain. Replace `isClickable` and `hasHoverHighlight` with an explicit native owner through `render`. Move action handlers off display-only wrappers, preserve accessible names, and keep nested controls outside the primary action. Use `render={<button type="button" disabled={disabled} />}` for an action and `render={<a href="/imports" />}` for navigation. DOM refs now point to the actual rendered part.

## Props

### Card.Root

<ParamField body="Root.backgroundColor" type="string">
  CSS background color of the outer card. Child sections have their own backgrounds.
</ParamField>

<ParamField body="Root.children" type="ReactNode">
  Card sections or other content.
</ParamField>

<ParamField body="Root.className" type="string">
  CSS class merged with the part class.
</ParamField>

<ParamField body="Root.fullWidth" type="boolean">
  Sets the card width to 100% of its container.
</ParamField>

<ParamField body="Root.ref" type="Ref<HTMLDivElement>">
  Ref to the default div, or the element supplied through render.
</ParamField>

<ParamField body="Root.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render callback composed through Base UI useRender. Supply native button or link attributes on the rendered owner.
</ParamField>

<ParamField body="Root.style" type="CSSProperties">
  Native styles applied to the rendered part.
</ParamField>

### Card.Header

<ParamField body="Header.children" type="ReactNode">
  Header content. Supply a Heading when the title should be a semantic heading.
</ParamField>

<ParamField body="Header.className" type="string">
  CSS class merged with the part class.
</ParamField>

<ParamField body="Header.ref" type="Ref<HTMLDivElement>">
  Ref to the default div, or the element supplied through render.
</ParamField>

<ParamField body="Header.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render callback composed through Base UI useRender. Supply native button or link attributes on the rendered owner.
</ParamField>

<ParamField body="Header.style" type="CSSProperties">
  Native styles applied to the rendered part.
</ParamField>

### Card.Content

<ParamField body="Content.children" type="ReactNode">
  Content displayed in the padded body.
</ParamField>

<ParamField body="Content.className" type="string">
  CSS class merged with the part class.
</ParamField>

<ParamField body="Content.divider" type="boolean">
  Adds a bottom border.
</ParamField>

<ParamField body="Content.ref" type="Ref<HTMLDivElement>">
  Ref to the default div, or the element supplied through render.
</ParamField>

<ParamField body="Content.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render callback composed through Base UI useRender. Supply native button or link attributes on the rendered owner.
</ParamField>

<ParamField body="Content.style" type="CSSProperties">
  Native styles applied to the rendered part.
</ParamField>

### Card.Footer

<ParamField body="Footer.children" type="ReactNode">
  Footer content, such as actions or supporting text.
</ParamField>

<ParamField body="Footer.className" type="string">
  CSS class merged with the part class.
</ParamField>

<ParamField body="Footer.divider" type="boolean" default="true">
  Shows the top border unless explicitly set to false.
</ParamField>

<ParamField body="Footer.ref" type="Ref<HTMLDivElement>">
  Ref to the default div, or the element supplied through render.
</ParamField>

<ParamField body="Footer.render" type="ReactElement<unknown, string | JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, {}>">
  Element or render callback composed through Base UI useRender. Supply native button or link attributes on the rendered owner.
</ParamField>

<ParamField body="Footer.style" type="CSSProperties">
  Native styles applied to the rendered part.
</ParamField>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.