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.

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

Props

Card props
PropTypeDescription
title (required)string

Card title

arrow (deprecated)"right" | "top-right"

Direction of the arrow indicator

DeprecatedAccepted but ignored. The arrow is derived from `href`: it appears only on a link that leaves the site, pointing top-right

classNamestring

Additional CSS classes

descriptionstring

Supporting copy displayed below the title

eyebrowstring | Array<string>

Small label displayed above the title. Several parts read as one bullet-separated line

hrefstring

Makes the card a clickable link

imageCardMediaSource

Image source URL, dark/light pair for theme-aware images, or a custom render function

logoCardMediaSource

Logo shown centered in the media area at its own aspect ratio, over `image` if there is one, which is then darkened

personsArray<CardPerson>

People to credit, shown as a stack of avatars below the title

renderLinkElementType<ComponentProps<"a"> & { href: string }>

Custom link component for client-side navigation (e.g. Next.js Link)

default value: "a"

sizesstring

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

default value: "auto, (min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"

tagsArray<CardTag>

Tags shown below the title, each optionally linking somewhere of its own

Required.

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
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        return (
5          <div className="gap-24 grid md:grid-cols-[repeat(auto-fill,minmax(320px,1fr))]">
6            <Card
7              title="Card with link and eyebrow"
8              eyebrow="Eyebrow"
9              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
10              href="#"
11            />
12            <Card
13              title="Card with link and no eyebrow"
14              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
15              href="#"
16            />
17            <Card
18              image={{
19                dark: "https://cdn.sanity.io/images/3do82whm/next/9847aab93fb3f7d1bd4cbc03f886a6375007974f-1730x1125.png",
20                light: "https://cdn.sanity.io/images/3do82whm/next/d931404fdb7f20ddc8481ce3de9bfd7270a30774-1730x1125.png",
21              }}
22              title="Card with different images for dark & light mode"
23              eyebrow="Eyebrow"
24              href="#"
25            />
26            <Card
27              title="Card with eyebrow and no link"
28              eyebrow="Eyebrow"
29              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
30            />
31            <Card
32              title="Card with no link and no eyebrow"
33              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
34            />
35            <Card
36              title="Card rendered with a custom link component"
37              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
38              renderLink={Link}
39            />
40          </div>
41        )
42      }
The arrow appears only on a link that leaves the site, and is not configurable
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        return (
5          <div className="gap-24 grid md:grid-cols-2">
6            <Card
7              title="Read the GROQ spec"
8              eyebrow="External"
9              description="An absolute URL, so the card gets the arrow."
10              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
11              href="https://sanity.io/docs/groq"
12            />
13            <Card
14              title="Read the docs"
15              eyebrow="Internal"
16              description="A relative path, so the card gets no arrow."
17              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
18              href="/docs"
19            />
20          </div>
21        )
22      }
With a description
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        return (
5          <div className="gap-24 grid md:grid-cols-2">
6            <Card
7              title="Content Lake"
8              eyebrow="Product"
9              description="A hosted, queryable datastore that keeps every version of every document."
10              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
11              href="#"
12            />
13            <Card
14              title="Structured content, explained"
15              description="Why modelling content as data outlasts modelling it as pages."
16              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
17              href="#"
18            />
19          </div>
20        )
21      }
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
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        return (
5          <div className="gap-24 grid md:grid-cols-2">
6            <Card
7              title="How Acme ships content faster"
8              eyebrow="Customer story"
9              logo="https://cdn.sanity.io/images/3do82whm/next/9a1e5c003b9c1a44f554c71f77694a12c5152eab-81x40.svg"
10              href="#"
11            />
12            <Card
13              title="A logo over an image, which is darkened for it"
14              eyebrow="Customer story"
15              logo="https://cdn.sanity.io/images/3do82whm/next/9a1e5c003b9c1a44f554c71f77694a12c5152eab-81x40.svg"
16              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
17              href="#"
18            />
19          </div>
20        )
21      }
The media area is reserved with no image or logo at all, so a grid of cards keeps its titles on one line
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        return (
5          <div className="gap-24 grid md:grid-cols-2">
6            <Card
7              title="A card with no media"
8              eyebrow="Guide"
9              description="No image and no logo, so the media area shows its texture alone."
10              href="#"
11            />
12            <Card
13              title="A card with an image beside it"
14              eyebrow="Guide"
15              description="Both titles start at the same height."
16              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
17              href="#"
18            />
19          </div>
20        )
21      }
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
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        const persons = [
5          { name: "Kevin Harwood", image: "https://cdn.sanity.io/images/3do82whm/next/30ff6e8265637f4efac13903d09284b6e350eda4-512x512.png" },
6          { name: "Saskia Bobinska", image: "https://cdn.sanity.io/images/3do82whm/next/20a113998dc15c76f6f7e31ce640524bdd6d5b5c-5184x3456.jpg" },
7          { name: "Bram Doppen", image: "https://cdn.sanity.io/images/3do82whm/next/1523a5ff4f2bc463580087d9b14946d9cea5208e-512x512.jpg" },
8        ]
9        return (
10          <div className="gap-24 grid md:grid-cols-2">
11            <Card
12              title="Querying with GROQ"
13              eyebrow={["Guide", "GROQ", "8 min read"]}
14              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."
15              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
16              href="#"
17              tags={[
18                { label: "GROQ", href: "#" },
19                { label: "Queries", href: "#" },
20                { label: "Beginner" },
21              ]}
22              persons={persons.slice(0, 1)}
23            />
24            <Card
25              title="Building a studio plugin"
26              eyebrow="Guide"
27              description="Package a custom input as a plugin others can install."
28              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
29              href="#"
30              tags={[{ label: "Studio" }, { label: "Plugins" }]}
31              persons={persons}
32            />
33          </div>
34        )
35      }
The footer shows whichever of avatars and tags is present — a card can have one without the other
1import { Card } from "@sanity/sanity-id/components/card"
2
3export default () => {
4        const persons = [
5          { name: "Kevin Harwood", image: "https://cdn.sanity.io/images/3do82whm/next/30ff6e8265637f4efac13903d09284b6e350eda4-512x512.png" },
6          { name: "Saskia Bobinska", image: "https://cdn.sanity.io/images/3do82whm/next/20a113998dc15c76f6f7e31ce640524bdd6d5b5c-5184x3456.jpg" },
7        ]
8        return (
9          <div className="gap-24 grid md:grid-cols-2">
10            <Card
11              title="Avatars, no tags"
12              eyebrow="Guide"
13              description="Credited people with no tags below them."
14              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
15              href="#"
16              persons={persons}
17            />
18            <Card
19              title="Tags, no avatars"
20              eyebrow="Guide"
21              description="Tags with no credited people above them."
22              image="https://cdn.sanity.io/images/3do82whm/next/79cafc547101a1761aa9b370ff2df86da9099ea9-4333x5127.png"
23              href="#"
24              tags={[{ label: "GROQ", href: "#" }, { label: "Queries" }]}
25            />
26          </div>
27        )
28      }