---
title: Card
description: Displays a card with header, content, and footer.
---

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
	"github.com/axadrn/shadcn-templ/v2/components/input"
	"github.com/axadrn/shadcn-templ/v2/components/label"
)

templ CardDemo() {
	@card.Card(card.Props{Class: "w-full max-w-sm"}) {
		@card.Header() {
			@card.Title() {
				Login to your account
			}
			@card.Description() {
				Enter your email below to login to your account
			}
			@card.Action() {
				@button.Button(button.Props{Variant: button.VariantLink}) {
					Sign Up
				}
			}
		}
		@card.Content() {
			<form>
				<div class="flex flex-col gap-6">
					<div class="grid gap-2">
						@label.Label(label.Props{For: "email"}) {
							Email
						}
						@input.Input(input.Props{
							ID:          "email",
							Type:        "email",
							Placeholder: "m@example.com",
							Attributes:  templ.Attributes{"required": true},
						})
					</div>
					<div class="grid gap-2">
						<div class="flex items-center">
							@label.Label(label.Props{For: "password"}) {
								Password
							}
							<a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline">
								Forgot your password?
							</a>
						</div>
						@input.Input(input.Props{
							ID:         "password",
							Type:       "password",
							Attributes: templ.Attributes{"required": true},
						})
					</div>
				</div>
			</form>
		}
		@card.Footer(card.FooterProps{Class: "flex-col gap-2"}) {
			@button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) {
				Login
			}
			@button.Button(button.Props{Variant: button.VariantOutline, Class: "w-full"}) {
				Login with Google
			}
		}
	}
}
```

## Installation

<CodeTabs>

<TabsList>
  <TabsTrigger value="cli">Command</TabsTrigger>
  <TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">

```bash
shadcn-templ add card
```

</TabsContent>

<TabsContent value="manual">

<Steps className="mb-0 pt-2">

<Step>Copy and paste the following code into your project.</Step>

<ComponentSource name="card" title="components/card/card.templ" />

<Step>Update the import paths to match your project setup.</Step>

</Steps>

</TabsContent>

</CodeTabs>

## Usage

```go showLineNumbers
import "github.com/axadrn/shadcn-templ/v2/components/card"
```

```templ showLineNumbers
@card.Card() {
	@card.Header() {
		@card.Title() {
			Card Title
		}
		@card.Description() {
			Card Description
		}
		@card.Action() {
			Card Action
		}
	}
	@card.Content() {
		<p>Card Content</p>
	}
	@card.Footer() {
		<p>Card Footer</p>
	}
}
```

## Composition

Use the following composition to build a `Card`:

```text
card.Card
├── card.Header
│   ├── card.Title
│   ├── card.Description
│   └── card.Action
├── card.Content
└── card.Footer
```

## Size

Use the `Size: card.SizeSm` prop to set the size of the card to small. The small size variant uses smaller spacing.

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
	"github.com/axadrn/shadcn-templ/v2/components/icon"
)

templ CardSmall() {
	@card.Card(card.Props{Size: card.SizeSm, Class: "mx-auto w-full max-w-xs"}) {
		@card.Header() {
			@card.Title() {
				Scheduled reports
			}
			@card.Description() {
				Weekly snapshots. No more manual exports.
			}
		}
		@card.Content() {
			<ul class="grid gap-2 py-2 text-sm">
				<li class="flex gap-2">
					@icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"})
					<span>Choose a schedule (daily, or weekly).</span>
				</li>
				<li class="flex gap-2">
					@icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"})
					<span>Send to channels or specific teammates.</span>
				</li>
				<li class="flex gap-2">
					@icon.ChevronRight(icon.Props{Class: "mt-0.5 size-4 shrink-0 text-muted-foreground"})
					<span>Include charts, tables, and key metrics.</span>
				</li>
			</ul>
		}
		@card.Footer(card.FooterProps{Class: "flex-col gap-2"}) {
			@button.Button(button.Props{Size: button.SizeSm, Class: "w-full"}) {
				Set up scheduled reports
			}
			@button.Button(button.Props{Variant: button.VariantOutline, Size: button.SizeSm, Class: "w-full"}) {
				See what's new
			}
		}
	}
}
```

## Spacing

In addition to the `Size` prop, you can use the `--card-spacing` CSS variable to control the spacing between sections and the inset of card parts.

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
	"github.com/axadrn/shadcn-templ/v2/components/input"
	"github.com/axadrn/shadcn-templ/v2/components/label"
	"github.com/axadrn/shadcn-templ/v2/components/togglegroup"
)

type spacingOption struct {
	Class string
	Label string
	Value string
}

var spacingOptions = []spacingOption{
	{"[--card-spacing:--spacing(4)]", "16px", "4"},
	{"[--card-spacing:--spacing(5)]", "20px", "5"},
	{"[--card-spacing:--spacing(6)]", "24px", "6"},
	{"[--card-spacing:--spacing(8)]", "32px", "8"},
}

templ CardSpacing() {
	<div class="mx-auto grid w-full max-w-sm gap-4" data-card-spacing-demo>
		@togglegroup.ToggleGroup(togglegroup.Props{
			Variant: togglegroup.VariantOutline,
			Size:    togglegroup.SizeSm,
			Class:   "justify-center",
		}) {
			for i, option := range spacingOptions {
				@togglegroup.Item(togglegroup.ItemProps{
					Value:      option.Value,
					Pressed:    i == 0,
					Attributes: templ.Attributes{"data-spacing-class": option.Class},
				}) {
					{ option.Label }
				}
			}
		}
		@card.Card(card.Props{Class: spacingOptions[0].Class}) {
			@card.Header() {
				@card.Title() {
					Login to your account
				}
				@card.Description() {
					Enter your email below to login to your account
				}
				@card.Action() {
					@button.Button(button.Props{Variant: button.VariantLink}) {
						Sign Up
					}
				}
			}
			@card.Content() {
				<form>
					<div class="flex flex-col gap-6">
						<div class="grid gap-2">
							@label.Label(label.Props{For: "email-spacing"}) {
								Email
							}
							@input.Input(input.Props{
								ID:          "email-spacing",
								Type:        "email",
								Placeholder: "m@example.com",
								Attributes:  templ.Attributes{"required": true},
							})
						</div>
						<div class="grid gap-2">
							<div class="flex items-center">
								@label.Label(label.Props{For: "password-spacing"}) {
									Password
								}
								<a href="#" class="ml-auto inline-block text-sm underline-offset-4 hover:underline">
									Forgot your password?
								</a>
							</div>
							@input.Input(input.Props{
								ID:         "password-spacing",
								Type:       "password",
								Attributes: templ.Attributes{"required": true},
							})
						</div>
					</div>
				</form>
			}
			@card.Footer(card.FooterProps{Class: "flex-col gap-2"}) {
				@button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) {
					Login
				}
				@button.Button(button.Props{Variant: button.VariantOutline, Class: "w-full"}) {
					Login with Google
				}
			}
		}
	</div>
	<script nonce={ templ.GetNonce(ctx) }>
		// The vanilla pendant of the demo's React state, the pressed toggle
		// swaps the spacing class on the card.
		document.addEventListener('toggle-change', (e) => {
			const demo = e.target.closest('[data-card-spacing-demo]');
			if (!demo || !e.detail.pressed) return;
			const cardEl = demo.querySelector('[data-slot="card"]');
			demo.querySelectorAll('[data-spacing-class]').forEach((t) => {
				cardEl.classList.remove(t.getAttribute('data-spacing-class'));
			});
			cardEl.classList.add(e.target.getAttribute('data-spacing-class'));
		});
	</script>
}
```

Use negative margins with `-mx-(--card-spacing)` to make content go edge to edge while keeping it aligned with the card inset. When the edge-to-edge content sits above a footer, use `-mb-(--card-spacing)` on `card.Content` to remove the section gap.

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
)

templ CardEdgeToEdge() {
	@card.Card(card.Props{Class: "mx-auto w-full max-w-sm"}) {
		@card.Header() {
			@card.Title() {
				Terms of Service
			}
			@card.Description() {
				Review the terms before accepting the agreement.
			}
		}
		@card.Content(card.ContentProps{Class: "-mb-(--card-spacing)"}) {
			<div class="-mx-(--card-spacing) max-h-48 space-y-4 overflow-y-scroll border-t bg-muted/50 px-(--card-spacing) py-4 text-sm leading-relaxed">
				<p>
					These terms govern your use of the workspace, including access to shared documents, project files, and collaboration tools.
				</p>
				<p>
					You are responsible for the content you upload and for ensuring that your team has the appropriate permissions to view or edit it.
				</p>
				<p>
					We may update features or limits as the service evolves. When those changes materially affect your workflow, we will notify your workspace administrators.
				</p>
				<p>
					By continuing, you agree to keep your account credentials secure and to follow your organization's acceptable use policies.
				</p>
			</div>
		}
		@card.Footer(card.FooterProps{Class: "justify-end gap-2"}) {
			@button.Button(button.Props{Variant: button.VariantOutline}) {
				Decline
			}
			@button.Button() {
				Accept
			}
		}
	}
}
```

## Image

Add an image before the card header to create a card with an image.

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/badge"
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
)

templ CardImage() {
	@card.Card(card.Props{Class: "relative mx-auto w-full max-w-sm pt-0"}) {
		<div class="absolute inset-0 z-30 aspect-video bg-black/35"></div>
		<img
			src="/assets/img/aspect_ratio_placeholder.jpeg"
			alt="Event cover"
			class="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
		/>
		@card.Header() {
			@card.Action() {
				@badge.Badge(badge.Props{Variant: badge.VariantSecondary}) {
					Featured
				}
			}
			@card.Title() {
				Design systems meetup
			}
			@card.Description() {
				A practical talk on component APIs, accessibility, and shipping faster.
			}
		}
		@card.Footer() {
			@button.Button(button.Props{Class: "w-full"}) {
				View Event
			}
		}
	}
}
```

## API Reference

### Card

The `Card` component is the root container for card content.

| Prop    | Type                    | Default       |
| ------- | ----------------------- | ------------- |
| `Size`  | `SizeDefault \| SizeSm` | `SizeDefault` |
| `Class` | `string`                | -             |

### Header

The `card.Header` component is used for a title, description, and optional action.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Title

The `card.Title` component is used for the card title.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Description

The `card.Description` component is used for helper text under the title.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Action

The `card.Action` component places content in the top-right of the header (for example, a button or a badge).

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Content

The `card.Content` component is used for the main card body.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Footer

The `card.Footer` component is used for actions and secondary content at the bottom of the card.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |
