Card

free

Card creates a bounded surface for related content, summaries, and nearby actions.

Preview
Dark
import { PiQuotes, PiStar } from 'react-icons/pi'
import Avatar from '@/components/ui/Avatar';
import Card from '@/components/ui/Card';

export default function UsageDemo() {
  return (
    <div className="w-80 max-w-full">
      <Card>
        <div className="space-y-4">
          <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-muted">
            <PiQuotes className="h-5 w-5 text-foreground" />
          </div>
          <p className="text-foreground">
            Every component matched spec on the first pass, and the whole
            project shipped two weeks ahead of schedule.
          </p>
          <div className="flex items-center justify-between gap-4 mt-12">
            <div className="flex items-center gap-2">
              <Avatar
                size="sm"
                src="/img/avatars/thumb-9.jpg"
                alt="Marcus Webb"
              />
              <div className="min-w-0">
                <div className="font-medium text-foreground">
                  Marcus Webb
                </div>
                <div className="text-xs text-muted-foreground">
                  Design Lead, Northwind
                </div>
              </div>
            </div>
            <div className="flex items-center gap-1 text-warning">
              <PiStar className="h-4 w-4" fill="currentColor" />
              <span className="font-medium text-foreground">5.0</span>
            </div>
          </div>
        </div>
      </Card>
    </div>
  );
}

Installation

Add this component with the NateUI CLI.

npx nateui@latest add Card

Examples

Basic

Preview
Dark
import Card from '@/components/ui/Card';

export default function BasicDemo() {
  return (
    <div className="w-80 max-w-full">
      <Card>
        <div className="space-y-2">
          <div className="font-semibold text-foreground">Customer profile</div>
          <p className="text-muted-foreground">
            Keep normal card content on the default body padding and let the
            shared surface provide the border, radius, and background.
          </p>
        </div>
      </Card>
    </div>
  );
}
Preview
Dark
import { PiDotsThree } from 'react-icons/pi'
import Avatar from '@/components/ui/Avatar';
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';

export default function HeaderFooterDemo() {
  return (
    <div className="w-96 max-w-full">
      <Card
        header={{
          bordered: false,
          content: (
            <span className="text-lg font-medium text-foreground">
              Team roadmap
            </span>
          ),
          extra: (
            <Button 
              size="sm"
              variant="ghost"
              icon={<PiDotsThree />} 
              aria-label="More options"
            />
          ),
        }}
        footer={{
          content: (
            <div className="flex items-center justify-between gap-4">
              <div className="flex items-center gap-2">
                <Avatar
                  size="sm"
                  src="/img/avatars/thumb-14.jpg"
                  alt="Priya Anand"
                />
                <span className="font-medium text-foreground">
                  Priya Anand
                </span>
              </div>
              <span className="text-muted-foreground">4h ago</span>
            </div>
          ),
        }}
      >
        <p className="text-muted-foreground">
          Outlines the next three releases, who is driving each one, and
          where the open risks are.
        </p>
      </Card>
    </div>
  );
}

Divided Body

Preview
Dark
import {
    PiArrowsOut,
    PiDotsThreeVertical,
    PiDownloadSimple,
    PiImage,
    PiLink,
    PiLinkSimple,
    PiPaperclip,
    PiPencilSimpleLine,
} from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';
import Tag from '@/components/ui/Tag';

const files = [
  {
    icon: PiLink,
    label: 'https://linear.app/acme/issue/pay-214',
    action: 'expand',
  },
  {
    icon: PiImage,
    label: 'Homepage redesign - final.png',
    action: 'expand',
  },
  {
    icon: PiPencilSimpleLine,
    label: 'Sprint retro notes',
    tasks: 2,
    action: 'download',
  },
  {
    icon: PiPaperclip,
    label: 'Vendor contract.pdf',
    tasks: 4,
    action: 'download',
  },
];

export default function DividedBodyDemo() {
  return (
    <div className="w-96 max-w-full">
      <Card bodyClass="p-0">
        <div className="divide-y">
          {files.map((file) => {
            const RowIcon = file.icon;
            const ActionIcon = file.action === 'expand' ? PiArrowsOut : PiDownloadSimple;

            return (
              <div
                key={file.label}
                className="flex items-center justify-between gap-4 p-4"
              >
                <div className="flex min-w-0 items-center gap-2">
                  <RowIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
                  <span className="truncate text-foreground">
                    {file.label}
                  </span>
                </div>
                <div className="flex shrink-0 items-center gap-2">
                  {file.tasks && (
                    <Tag prefix={<PiLinkSimple className="mr-1 h-3.5 w-3.5" />}>
                      {file.tasks} linked tasks
                    </Tag>
                  )}
                  <Button
                    variant="ghost"
                    size="sm"
                    icon={<ActionIcon />}
                    aria-label={
                      file.action === 'expand' ? 'Expand' : 'Download'
                    }
                  />
                  <Button
                    variant="ghost"
                    size="sm"
                    icon={<PiDotsThreeVertical />}
                    aria-label="More options"
                  />
                </div>
              </div>
            );
          })}
        </div>
      </Card>
    </div>
  );
}

Borderless

Preview
Dark
import { PiMapPin } from 'react-icons/pi'
import Card from '@/components/ui/Card';
import Tag from '@/components/ui/Tag';

export default function BorderlessDemo() {
  return (
    <div className="w-96 max-w-full">
      <Card bordered={false} bodyClass="p-0" className="overflow-hidden">
        <div className="relative aspect-4/5 w-full">
          <img
            src="/img/thumbs/misc/img-6.png"
            alt=""
            className="absolute inset-0 h-full w-full object-cover"
          />
          <Tag
            prefix={<PiMapPin className="mr-1 h-3.5 w-3.5" />}
            className="absolute left-4 top-4 border-transparent bg-background/90 text-foreground backdrop-blur-sm"
          >
            Featured Stay
          </Tag>
          <div className="absolute inset-x-0 bottom-0 bg-linear-to-t from-black/85 via-black/40 to-transparent p-4 pt-16">
            <div className="text-lg font-semibold text-white">
              Two Days in Kyoto
            </div>
            <p className="mt-1 text-white/80">
              Quiet ryokans, short train rides, and temples that open before
              the crowds.
            </p>
          </div>
        </div>
      </Card>
    </div>
  );
}

Clickable

Preview
Dark
import Card from '@/components/ui/Card';
import { ChevronRight } from '@/components/ui/Icons';

export default function ClickableDemo() {
  return (
    <div className="w-full max-w-112.5">
      <Card clickable bodyClass="p-0" className="transition-transform hover:scale-[1.02] hover:bg-muted/30">
        <div className="flex items-center gap-4 p-4">
          <div className="flex h-28 w-28 shrink-0 items-center justify-center rounded-2xl bg-muted border p-2">
            <img
              src="/img/thumbs/misc/img-2.png"
              alt=""
              className="h-full w-full rounded-lg object-cover"
            />
          </div>
          <div className="min-w-0 flex-1">
            <div className="font-semibold text-foreground text-base">Integrations</div>
            <p className="mt-1 text-sm text-muted-foreground">
              Connect the tools your team already uses without leaving the
              dashboard.
            </p>
          </div>
          <ChevronRight className="shrink-0 text-xl" />
        </div>
      </Card>
    </div>
  );
}

Media

Preview
Dark
import { ChevronRight } from '@/components/ui/Icons';
import Button from '@/components/ui/Button';
import Card from '@/components/ui/Card';

export default function MediaDemo() {
  return (
    <div className="w-80 max-w-full">
      <Card bodyClass="p-0" className="overflow-hidden">
        <img
          src="/img/thumbs/misc/img-1.png"
          alt=""
          className="aspect-video w-full object-cover"
        />
        <div className="space-y-8 p-4">
          <div className="space-y-4">
            <div className="text-base font-semibold text-foreground">
              Layout patterns that cut checkout
            </div>
            <p className="text-muted-foreground">
              A walkthrough of the spacing, copy, and validation choices
              that move the needle on multi-step forms.
            </p>
          </div>
          <Button
            block
            variant="solid"
            icon={<ChevronRight />}
            iconAlignment="end"
          >
            Learn More
          </Button>
        </div>
      </Card>
    </div>
  );
}

API

Card accepts the props below plus native props for the root div.

PropDescriptionTypeDefault
bodyClassClass names applied to the card body section.string-
borderedUses the default bordered card surface. Pass false for the shadow card treatment.booleantrue
childrenMain card body content.ReactNode-
classNameClass names applied to the card root.string-
clickableAdds pointer cursor and disables text selection on the card root.booleanfalse
footerOptional footer section configuration.CardFooterConfig{}
headerOptional header section configuration.CardHeaderConfig{}
onClickCalled when the card root is clicked.CardClickHandler-
refRef forwarded to the card root.Ref<HTMLDivElement>-
...nativeDivPropsNative props for the root div.NativeCardDivProps-

Types

type CardHeaderConfig = {
  content?: string | ReactNode;
  className?: string;
  bordered?: boolean;
  extra?: string | ReactNode;
};
 
type CardFooterConfig = {
  content?: string | ReactNode;
  className?: string;
  bordered?: boolean;
};
 
type CardClickHandler = (event: MouseEvent<HTMLDivElement>) => void;
type NativeCardDivProps = ComponentPropsWithRef<'div'>;