Card
Displays a card with header, content, and footer.
Login to your account
Enter your email below to login to your account
import { css } from 'remix/component'
import { Button } from '#app/ui/button.tsx'
import {
Card,
CardAction,import { css } from 'remix/component'
import { Button } from '#app/ui/button.tsx'
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '#app/ui/card.tsx'
import { Input } from '#app/ui/input.tsx'
import { Label } from '#app/ui/label.tsx'
const field = css({ display: 'grid', gap: '0.5rem' })
const fullWidth = css({ width: '100%' })
export function CardDemo() {
return () => (
<Card mix={css({ width: '100%', maxWidth: '24rem' })}>
<CardHeader>
<CardTitle>Login to your account</CardTitle>
<CardDescription>Enter your email below to login to your account</CardDescription>
<CardAction>
<Button variant="link">Sign Up</Button>
</CardAction>
</CardHeader>
<CardContent>
<form>
<div mix={css({ display: 'flex', flexDirection: 'column', gap: '1.5rem' })}>
<div mix={field}>
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="m@example.com" required />
</div>
<div mix={field}>
<div mix={css({ display: 'flex', alignItems: 'center' })}>
<Label htmlFor="password">Password</Label>
<a
href="#"
mix={css({
marginLeft: 'auto',
display: 'inline-block',
fontSize: '0.875rem',
lineHeight: 'calc(1.25 / 0.875)',
textUnderlineOffset: '4px',
'&:hover': { textDecorationLine: 'underline' },
})}
>
Forgot your password?
</a>
</div>
<Input id="password" type="password" required />
</div>
</div>
</form>
</CardContent>
<CardFooter mix={css({ flexDirection: 'column', gap: '0.5rem' })}>
<Button type="submit" mix={fullWidth}>
Login
</Button>
<Button variant="outline" mix={fullWidth}>
Login with Google
</Button>
</CardFooter>
</Card>
)
}Installation
$ bunx --bun shadcn@latest add https://www.shadcn-remix.com/r/card.json$ npx shadcn@latest add https://www.shadcn-remix.com/r/card.json$ pnpm dlx shadcn@latest add https://www.shadcn-remix.com/r/card.json$ yarn dlx shadcn@latest add https://www.shadcn-remix.com/r/card.jsonInstall the following dependencies:
$ bun add remix$ npm install remix$ pnpm add remix$ yarn add remixCopy and paste the following code into your project.
import { css, type Handle, type Props } from 'remix/component'
const cardStyles = css({
display: 'flex',
flexDirection: 'column',
gap: '1.5rem',
borderRadius: 'calc(var(--radius) + 4px)',
borderWidth: '1px',
backgroundColor: 'var(--card)',
paddingBlock: '1.5rem',
color: 'var(--card-foreground)',
'--tw-shadow': '0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
boxShadow: 'var(--tw-ring-shadow), var(--tw-shadow)',
})
const cardHeaderStyles = css({
containerType: 'inline-size',
containerName: 'card-header',
display: 'grid',
gridAutoRows: 'min-content',
gridTemplateRows: 'auto auto',
alignItems: 'start',
gap: '0.5rem',
paddingInline: '1.5rem',
'&:has([data-slot="card-action"])': { gridTemplateColumns: '1fr auto' },
})
const cardTitleStyles = css({ lineHeight: 1, fontWeight: 600 })
const cardDescriptionStyles = css({
fontSize: '0.875rem',
lineHeight: 'calc(1.25 / 0.875)',
color: 'var(--muted-foreground)',
})
const cardActionStyles = css({
gridColumnStart: '2',
gridRow: 'span 2 / span 2',
gridRowStart: '1',
alignSelf: 'start',
justifySelf: 'end',
})
const cardContentStyles = css({ paddingInline: '1.5rem' })
const cardFooterStyles = css({
display: 'flex',
alignItems: 'center',
paddingInline: '1.5rem',
})
export function Card(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card" {...props} mix={[cardStyles, mix]} />
}
}
export function CardHeader(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-header" {...props} mix={[cardHeaderStyles, mix]} />
}
}
export function CardTitle(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-title" {...props} mix={[cardTitleStyles, mix]} />
}
}
export function CardDescription(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-description" {...props} mix={[cardDescriptionStyles, mix]} />
}
}
export function CardAction(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-action" {...props} mix={[cardActionStyles, mix]} />
}
}
export function CardContent(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-content" {...props} mix={[cardContentStyles, mix]} />
}
}
export function CardFooter(handle: Handle<Props<'div'>>) {
return () => {
let { mix, ...props } = handle.props
return <div data-slot="card-footer" {...props} mix={[cardFooterStyles, mix]} />
}
}Update the import paths to match your project setup.
Usage
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '#app/ui/card.tsx'<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card Description</CardDescription>
<CardAction>Card Action</CardAction>
</CardHeader>
<CardContent>
<p>Card Content</p>
</CardContent>
<CardFooter>
<p>Card Footer</p>
</CardFooter>
</Card>Composition
Use the following composition to build a Card:
Card
├── CardHeader
│ ├── CardTitle
│ ├── CardDescription
│ └── CardAction
├── CardContent
└── CardFooterExamples
With Form
Create project
Deploy your new project in one-click.
import { css } from 'remix/component'
import { Button } from '#app/ui/button.tsx'
import {
Card,
CardContent,import { css } from 'remix/component'
import { Button } from '#app/ui/button.tsx'
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '#app/ui/card.tsx'
import { Input } from '#app/ui/input.tsx'
import { Label } from '#app/ui/label.tsx'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '#app/ui/select.tsx'
const field = css({ display: 'flex', flexDirection: 'column', gap: '0.75rem' })
export function CardWithForm() {
return () => (
<Card mix={css({ width: '350px' })}>
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one-click.</CardDescription>
</CardHeader>
<CardContent>
<form>
<div mix={css({ display: 'grid', width: '100%', alignItems: 'center', gap: '1.5rem' })}>
<div mix={field}>
<Label htmlFor="name">Name</Label>
<Input id="name" placeholder="Name of your project" />
</div>
<div mix={field}>
<Label htmlFor="framework">Framework</Label>
<Select>
<SelectTrigger id="framework" mix={css({ width: '100%' })}>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="next">Next.js</SelectItem>
<SelectItem value="sveltekit">SvelteKit</SelectItem>
<SelectItem value="astro">Astro</SelectItem>
<SelectItem value="nuxt">Nuxt.js</SelectItem>
</SelectContent>
</Select>
</div>
</div>
</form>
</CardContent>
<CardFooter mix={css({ display: 'flex', justifyContent: 'space-between' })}>
<Button variant="outline">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
</Card>
)
}API Reference
Card
The root container for card content.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardHeader
Holds the title, description, and optional action.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardTitle
The card title.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardDescription
Helper text under the title.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardAction
Places content in the top-right of the header (for example, a button or a badge).
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardContent
The main card body.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |
CardFooter
Actions and secondary content at the bottom of the card.
| Prop | Type | Default |
|---|---|---|
mix | MixValue | - |