---
title: "Card"
description: "A linkable content card combining an image, an eyebrow, and a title. The media area is reserved whether or not an `image` or `logo` is given, so cards in a row line their titles up even when only some of them have artwork."
canonical_url: "https://sanity-id-docs.sanity.build/sanity-id/components/card"
md_url: "https://sanity-id-docs.sanity.build/sanity-id/components/card.md"
docs_index: "https://sanity-id-docs.sanity.build/sanity-id/llms.txt"
version: "1.15.2"
---

> Part of the Sanity ID reference. The index of every page is at https://sanity-id-docs.sanity.build/sanity-id/llms.txt.

# Card

A linkable content card combining an image, an eyebrow, and a title. The media area is reserved whether or not an `image` or `logo` is given, so cards in a row line their titles up even when only some of them have artwork.

```tsx
import { Card } from "@sanity/sanity-id/components/card"
```

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `title` | `string` | yes | — | Card title |
| `arrow` | `"right" \| "top-right"` | no | — | **Deprecated.** Accepted but ignored. The arrow is derived from `href`: it appears only on a link that leaves the site, pointing top-right — Direction of the arrow indicator |
| `className` | `string` | no | — | Additional CSS classes |
| `description` | `string` | no | — | Supporting copy displayed below the title |
| `eyebrow` | `string \| Array<string>` | no | — | Small label displayed above the title. Several parts read as one bullet-separated line |
| `href` | `string` | no | — | Makes the card a clickable link |
| `image` | `CardMediaSource` | no | — | Image source URL, dark/light pair for theme-aware images, or a custom render function |
| `logo` | `CardMediaSource` | no | — | Logo shown centered in the media area at its own aspect ratio, over `image` if there is one, which is then darkened |
| `persons` | `Array<CardPerson>` | no | — | People to credit, shown as a stack of avatars below the title |
| `renderLink` | `ElementType<ComponentProps<"a"> & { href: string }>` | no | `"a"` | Custom link component for client-side navigation (e.g. Next.js Link) |
| `sizes` | `string` | no | `"auto, (min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"` | How wide the image renders, for the browser to pick a srcset candidate against. Worth setting when the card is not in a one/two/three-column grid |
| `tags` | `Array<CardTag>` | no | — | Tags shown below the title, each optionally linking somewhere of its own |

`CardMediaSource`: `string | { dark: string; light: string } | ((props: ComponentProps<"img">) => ReactNode)`

`CardPerson`: `{ name?: string; image: string | FunctionComponent<ComponentProps<"img">> }`

`CardTag`: `{ label: string; href?: string }`

## Usage

### Image, eyebrow, and title

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	return (
		<div className="gap-24 grid md:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]">
			<Card
				title="Card with link and eyebrow"
				eyebrow="Eyebrow"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
			<Card
				title="Card with link and no eyebrow"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
			<Card
				image={{
					dark: "https://cdn.sanity.io/images/3do82whm/next/9847aab93fb3f7d1bd4cbc03f886a6375007974f-1730x1125.png",
					light: "https://cdn.sanity.io/images/3do82whm/next/d931404fdb7f20ddc8481ce3de9bfd7270a30774-1730x1125.png",
				}}
				title="Card with different images for dark & light mode"
				eyebrow="Eyebrow"
				href="#"
			/>
			<Card
				title="Card with eyebrow and no link"
				eyebrow="Eyebrow"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
			/>
			<Card
				title="Card with no link and no eyebrow"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
			/>
			<Card
				title="Card rendered with a custom link component"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				renderLink={Link}
			/>
		</div>
	)
}
```

### The arrow appears only on a link that leaves the site, and is not configurable

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="Read the GROQ spec"
				eyebrow="External"
				description="An absolute URL, so the card gets the arrow."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="https://sanity.io/docs/groq"
			/>
			<Card
				title="Read the docs"
				eyebrow="Internal"
				description="A relative path, so the card gets no arrow."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="/docs"
			/>
		</div>
	)
}
```

### With a description

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="Content Lake"
				eyebrow="Product"
				description="A hosted, queryable datastore that keeps every version of every document."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
			<Card
				title="Structured content, explained"
				description="Why modelling content as data outlasts modelling it as pages."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
		</div>
	)
}
```

### With a logo, centered in the same media area so mixed grids stay aligned. Given an image too, the logo sits over it and the image is darkened

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="How Acme ships content faster"
				eyebrow="Customer story"
				logo="https://cdn.sanity.io/images/3do82whm/next/9a1e5c003b9c1a44f554c71f77694a12c5152eab-81x40.svg"
				href="#"
			/>
			<Card
				title="A logo over an image, which is darkened for it"
				eyebrow="Customer story"
				logo="https://cdn.sanity.io/images/3do82whm/next/9a1e5c003b9c1a44f554c71f77694a12c5152eab-81x40.svg"
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
		</div>
	)
}
```

### The media area is reserved with no image or logo at all, so a grid of cards keeps its titles on one line

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="A card with no media"
				eyebrow="Guide"
				description="No image and no logo, so the media area shows its texture alone."
				href="#"
			/>
			<Card
				title="A card with an image beside it"
				eyebrow="Guide"
				description="Both titles start at the same height."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
			/>
		</div>
	)
}
```

### With tags and credited people. Tags and avatars sit outside the card's link, so a tag can link somewhere of its own. The first card's eyebrow is an array, joined into one bullet-separated line

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	const persons = [
		{ name: "Kevin Harwood", image: "https://cdn.sanity.io/images/3do82whm/next/30ff6e8265637f4efac13903d09284b6e350eda4-512x512.png" },
		{ name: "Saskia Bobinska", image: "https://cdn.sanity.io/images/3do82whm/next/20a113998dc15c76f6f7e31ce640524bdd6d5b5c-5184x3456.jpg" },
		{ name: "Bram Doppen", image: "https://cdn.sanity.io/images/3do82whm/next/1523a5ff4f2bc463580087d9b14946d9cea5208e-512x512.jpg" },
	]
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="Querying with GROQ"
				eyebrow={["Guide", "GROQ", "8 min read"]}
				description="Projections, filters, joins, and ordering in one query language — long enough here to show where a description clamps to two lines and trails off."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
				tags={[
					{ label: "GROQ", href: "#" },
					{ label: "Queries", href: "#" },
					{ label: "Beginner" },
				]}
				persons={persons.slice(0, 1)}
			/>
			<Card
				title="Building a studio plugin"
				eyebrow="Guide"
				description="Package a custom input as a plugin others can install."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
				tags={[{ label: "Studio" }, { label: "Plugins" }]}
				persons={persons}
			/>
		</div>
	)
}
```

### The footer shows whichever of avatars and tags is present — a card can have one without the other

```tsx
import { Card } from "@sanity/sanity-id/components/card"

export default () => {
	const persons = [
		{ name: "Kevin Harwood", image: "https://cdn.sanity.io/images/3do82whm/next/30ff6e8265637f4efac13903d09284b6e350eda4-512x512.png" },
		{ name: "Saskia Bobinska", image: "https://cdn.sanity.io/images/3do82whm/next/20a113998dc15c76f6f7e31ce640524bdd6d5b5c-5184x3456.jpg" },
	]
	return (
		<div className="gap-24 grid md:grid-cols-2">
			<Card
				title="Avatars, no tags"
				eyebrow="Guide"
				description="Credited people with no tags below them."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
				persons={persons}
			/>
			<Card
				title="Tags, no avatars"
				eyebrow="Guide"
				description="Tags with no credited people above them."
				image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
				href="#"
				tags={[{ label: "GROQ", href: "#" }, { label: "Queries" }]}
			/>
		</div>
	)
}
```

