Card

Minimalist cards for grouping content.

Boost Your Productivity

Learn the best techniques to manage your time and code smarter, not harder.

Discover tips, tools, and tricks that help developers stay focused and efficient. From keyboard shortcuts to workflow optimizations, elevate your coding game.

card-demo.tsx
1import { Button } from '@/components/ui/button';
2import {
3 Card,
4 CardContent,
5 CardDescription,
6 CardFooter,
7 CardHeader,
8 CardTitle,
9} from '@/components/ui/card';
10
11export function CardDemo() {
12 return (
13 <Card className="w-full max-w-sm shadow-lg transition-shadow duration-300 hover:shadow-xl">
14 <CardHeader>
15 <CardTitle>Boost Your Productivity</CardTitle>
16 <CardDescription>
17 Learn the best techniques to manage your time and code smarter, not harder.
18 </CardDescription>
19 </CardHeader>
20 <CardContent className="text-muted-foreground text-sm">
21 <p>
22 Discover tips, tools, and tricks that help developers stay focused and efficient. From
23 keyboard shortcuts to workflow optimizations, elevate your coding game.
24 </p>
25 </CardContent>
26 <CardFooter className="flex justify-end gap-2">
27 <Button variant="secondary">Learn More</Button>
28 <Button variant="ghost">Subscribe</Button>
29 </CardFooter>
30 </Card>
31 );
32}

Installation

Copy and paste the following code into your project.

card-source.tsx
'use client';
import { cva, type VariantProps } from 'class-variance-authority';
import { forwardRef } from 'react';
import { cn } from '../../../lib/cn';
const cardVariants = cva(
'rounded-lg transition-all duration-200 h-full flex flex-col justify-between',
{
variants: {
variant: {
default: 'bg-card text-card-foreground border border-border shadow-sm',
elevated: 'bg-card text-card-foreground border-0 shadow-lg',
outline: 'bg-card text-card-foreground border-2 border-border shadow-none',
ghost: 'bg-transparent text-card-foreground border-0 shadow-none',
},
hoverable: {
true: 'cursor-pointer hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 active:shadow-sm',
},
clickable: {
true: 'cursor-pointer hover:shadow-md hover:-translate-y-0.5 active:translate-y-0 active:shadow-sm',
},
gradient: {
true: 'bg-gradient-to-br from-card to-card/80',
},
},
defaultVariants: {
variant: 'default',
hoverable: false,
clickable: false,
gradient: false,
},
},
);
const cardHeaderVariants = cva('flex flex-col space-y-1.5', {
variants: {
compact: {
true: 'p-4',
false: 'p-6',
},
},
defaultVariants: {
compact: false,
},
});
const cardTitleVariants = cva('text-lg font-semibold leading-none tracking-tight');
const cardDescriptionVariants = cva('text-sm text-muted-foreground leading-relaxed');
const cardContentVariants = cva('flex-1', {
variants: {
compact: {
true: 'p-4 pt-0',
false: 'p-6 pt-0',
},
},
defaultVariants: {
compact: false,
},
});
const cardFooterVariants = cva('flex items-center', {
variants: {
align: {
start: 'justify-start',
center: 'justify-center',
end: 'justify-end',
between: 'justify-between',
},
compact: {
true: 'p-4 pt-0',
false: 'p-6 pt-0',
},
},
defaultVariants: {
align: 'start',
compact: false,
},
});
interface CardProps
extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {
as?: React.ElementType;
}
interface CardHeaderProps
extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardHeaderVariants> {}
interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
}
interface CardDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {}
interface CardContentProps
extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardContentVariants> {}
interface CardFooterProps
extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardFooterVariants> {}
const Card = forwardRef<HTMLDivElement, CardProps>(
(
{
className,
variant = 'default',
hoverable = false,
clickable = false,
gradient = false,
as: Component = 'div',
children,
...props
},
ref,
) => {
return (
<Component
ref={ref}
className={cn(cardVariants({ variant, hoverable, clickable, gradient }), className)}
{...(clickable && {
role: 'button',
tabIndex: 0,
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
(e.currentTarget as HTMLElement).click();
}
},
})}
{...props}
>
{children}
</Component>
);
},
);
Card.displayName = 'Card';
const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
({ className, compact, children, ...props }, ref) => {
return (
<div ref={ref} className={cn(cardHeaderVariants({ compact }), className)} {...props}>
{children}
</div>
);
},
);
CardHeader.displayName = 'CardHeader';
const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(
({ className, as: Component = 'h3', children, ...props }, ref) => {
return (
<Component ref={ref} className={cn(cardTitleVariants(), className)} {...props}>
{children}
</Component>
);
},
);
CardTitle.displayName = 'CardTitle';
const CardDescription = forwardRef<HTMLParagraphElement, CardDescriptionProps>(
({ className, children, ...props }, ref) => {
return (
<p ref={ref} className={cn(cardDescriptionVariants(), className)} {...props}>
{children}
</p>
);
},
);
CardDescription.displayName = 'CardDescription';
const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
({ className, compact, children, ...props }, ref) => {
return (
<div ref={ref} className={cn(cardContentVariants({ compact }), className)} {...props}>
{children}
</div>
);
},
);
CardContent.displayName = 'CardContent';
const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
({ className, align, compact, children, ...props }, ref) => {
return (
<div ref={ref} className={cn(cardFooterVariants({ align, compact }), className)} {...props}>
{children}
</div>
);
},
);
CardFooter.displayName = 'CardFooter';
export {
Card,
CardContent,
cardContentVariants,
CardDescription,
cardDescriptionVariants,
CardFooter,
cardFooterVariants,
CardHeader,
cardHeaderVariants,
CardTitle,
cardTitleVariants,
cardVariants,
};
export type {
CardContentProps,
CardDescriptionProps,
CardFooterProps,
CardHeaderProps,
CardProps,
CardTitleProps,
};

Make sure to update the import paths to match your project structure.

Usage

import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
</CardHeader>
<CardContent>
<p>Card Content</p>
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>

Variants

Outline

Cards with a prominent border and no shadow.

Outline Card

Clean and defined with a bold border

Outline cards work great for forms, settings, and structured content.

card-outline-demo.tsx
1import {
2 Card,
3 CardContent,
4 CardDescription,
5 CardHeader,
6 CardTitle,
7} from '@/components/ui/card';
8
9export function CardOutlineDemo() {
10 return (
11 <Card variant="outline" className="w-[350px]">
12 <CardHeader>
13 <CardTitle>Outline Card</CardTitle>
14 <CardDescription>Clean and defined with a bold border</CardDescription>
15 </CardHeader>
16 <CardContent>
17 <p>Outline cards work great for forms, settings, and structured content.</p>
18 </CardContent>
19 </Card>
20 );
21}

Ghost

Transparent cards without border or shadow.

Ghost Card

Subtle and unobtrusive

Ghost cards are perfect for minimalist designs where you want grouping without visual weight.

card-ghost-demo.tsx
1import {
2 Card,
3 CardContent,
4 CardDescription,
5 CardHeader,
6 CardTitle,
7} from '@/components/ui/card';
8
9export function CardGhostDemo() {
10 return (
11 <Card variant="ghost" className="w-[350px]">
12 <CardHeader>
13 <CardTitle>Ghost Card</CardTitle>
14 <CardDescription>Subtle and unobtrusive</CardDescription>
15 </CardHeader>
16 <CardContent>
17 <p>
18 Ghost cards are perfect for minimalist designs where you want grouping without visual
19 weight.
20 </p>
21 </CardContent>
22 </Card>
23 );
24}

Compact

Cards with reduced padding for dense layouts.

Regular Spacing

Default padding

This card uses the default padding for comfortable spacing.

Compact Spacing

Reduced padding

This card uses compact padding for denser layouts.

card-compact-demo.tsx
1import { Button } from '@/components/ui/button';
2import {
3 Card,
4 CardContent,
5 CardDescription,
6 CardFooter,
7 CardHeader,
8 CardTitle,
9} from '@/components/ui/card';
10
11export function CardCompactDemo() {
12 return (
13 <div className="grid gap-4">
14 <Card className="w-full">
15 <CardHeader>
16 <CardTitle>Regular Spacing</CardTitle>
17 <CardDescription>Default padding</CardDescription>
18 </CardHeader>
19 <CardContent>
20 <p>This card uses the default padding for comfortable spacing.</p>
21 </CardContent>
22 <CardFooter>
23 <Button variant="secondary">Action</Button>
24 </CardFooter>
25 </Card>
26 <Card className="w-full">
27 <CardHeader compact>
28 <CardTitle>Compact Spacing</CardTitle>
29 <CardDescription>Reduced padding</CardDescription>
30 </CardHeader>
31 <CardContent compact>
32 <p>This card uses compact padding for denser layouts.</p>
33 </CardContent>
34 <CardFooter compact>
35 <Button variant="secondary">Action</Button>
36 </CardFooter>
37 </Card>
38 </div>
39 );
40}

API Reference

Card

PropTypeDefaultDescription
variant"default" | "outline" | "ghost""default"Visual variant of the card.
asReact.ElementType'div'Changes the root element of the card.
classNamestringAdditional classes.
childrenReact.ReactNodeCard content.

CardHeader

PropTypeDefaultDescription
compactbooleanfalseReduces top and bottom padding of the header.
classNamestringAdditional classes.
childrenReact.ReactNodeHeader content.

CardTitle

PropTypeDefaultDescription
as'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6''h3'HTML tag for the card title.
classNamestringAdditional classes.
childrenReact.ReactNodeTitle content.

CardDescription

PropTypeDefaultDescription
classNamestringAdditional classes.
childrenReact.ReactNodeDescription text.

CardContent

PropTypeDefaultDescription
compactbooleanfalseReduces content padding.
classNamestringAdditional classes.
childrenReact.ReactNodeMain content of the card.

CardFooter

PropTypeDefaultDescription
align'start' | 'center' | 'end' | 'between''start'Controls horizontal alignment of the footer.
compactbooleanfalseReduces footer padding.
classNamestringAdditional classes.
childrenReact.ReactNodeFooter content.