Card

A versatile card component with header, content, footer, and image support

Brook

New

Defining a brook

A small, natural stream of fresh water flowing along a course towards a river, lake, or sea.

import {
  Card,
  CardHeader,
  CardTitle,
  CardAction,
  CardDescription,
  CardContent,
  CardFooter,
  CardIcon,
} from "@/components/ui/card/card";
import { Droplets } from "lucide-react";
import { Button } from "@/components/ui/button/button";
import { Badge } from "@/components/ui/badge/badge";
import styles from "./card-demo.module.css";

export default function CardDemo() {
  return (
    <Card className={styles.card}>
      <CardHeader>
        <div>
          <CardIcon>
            <Droplets />
          </CardIcon>
          <CardTitle>Brook</CardTitle>
        </div>

        <CardAction>
          <Badge size="sm" variant="secondary">
            New
          </Badge>
        </CardAction>

        <CardDescription>Defining a brook</CardDescription>
      </CardHeader>
      <CardContent>
        <p>A small, natural stream of fresh water flowing along a course towards a river, lake, or sea.</p>
      </CardContent>
      <CardFooter>
        <Button size="sm">Click me</Button>
      </CardFooter>
    </Card>
  );
}

Installation

npx shadcn@latest add https://roiui.com/r/card.json

Anatomy

anatomy
import { Card, CardHeader, CardTitle, CardAction,  CardDescription, CardContent, CardFooter } from '../ui/card'

<Card>
  <CardHeader>
      <CardTitle />
      <CardAction />
      <CardDescription />
  </CardHeader>
  <CardContent />
  <CardFooter />
</Card>

Examples

Image Card

Mountain landscape

Mountain Adventure

Experience the beauty of mountain landscapes with guided tours and hiking adventures.

import { Card, CardTitle, CardContent, CardImage, CardFooter } from "@/components/ui/card/card";
import { Button } from "@/components/ui/button/button";
import styles from "./card-image.module.css";

export default function CardImageDemo() {
  return (
    <Card variant="lift" className={styles.card}>
      <CardImage
        src="https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=560&h=315&fit=crop&crop=center"
        alt="Mountain landscape"
      />

      <CardContent>
        <CardTitle className={styles.title}>Mountain Adventure</CardTitle>
        <p className={styles.description}>
          Experience the beauty of mountain landscapes with guided tours and hiking adventures.
        </p>
      </CardContent>

      <CardFooter>
        <Button showArrow size="sm">
          Learn More
        </Button>
      </CardFooter>
    </Card>
  );
}

Login Card

"use client";
import { Card, CardHeader, CardTitle, CardContent, CardFooter } from "@/components/ui/card/card";
import { Field, FieldLabel, FieldControl, FieldError } from "@/components/ui/field/field";
import { useState } from "react";
import { Button } from "@/components/ui/button/button";
import styles from "./card-login.module.css";

export default function CardLoginDemo() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [errors, setErrors] = useState<{ email?: string; password?: string }>({});

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();

    const newErrors: { email?: string; password?: string } = {};

    if (!email) {
      newErrors.email = "Email is required";
    } else if (!/\S+@\S+\.\S+/.test(email)) {
      newErrors.email = "Email is invalid";
    }

    if (!password) {
      newErrors.password = "Password is required";
    } else if (password.length < 6) {
      newErrors.password = "Password must be at least 6 characters";
    }

    setErrors(newErrors);

    if (Object.keys(newErrors).length === 0) {
      console.log("Login successful!", { email, password });
    }
  };

  return (
    <Card className={styles.card}>
      <CardHeader>
        <CardTitle>Sign In</CardTitle>
      </CardHeader>
      <CardContent>
        <form onSubmit={handleSubmit} className={styles.form}>
          <Field>
            <FieldLabel>Email</FieldLabel>
            <FieldControl
              type="email"
              placeholder="Enter your email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />

            {errors.email && <FieldError>{errors.email}</FieldError>}
          </Field>

          <Field>
            <FieldLabel>Password</FieldLabel>
            <FieldControl
              type="password"
              placeholder="Enter your password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
            {errors.password && <FieldError>{errors.password}</FieldError>}
          </Field>
        </form>
      </CardContent>
      <CardFooter className={styles.footer}>
        <Button onClick={handleSubmit} className={styles.button}>
          Sign In
        </Button>
        <Button variant="ghost" className={styles.button}>
          Forgot Password?
        </Button>
      </CardFooter>
    </Card>
  );
}

Props

Card

PropTypeDefault
variant
"default" | "lift""default"
className
string-
children
React.ReactNode-

CardHeader

PropTypeDefault
className
string-
children
React.ReactNode-

CardTitle

PropTypeDefault
className
string-
children
React.ReactNode-

CardDescription

PropTypeDefault
className
string-
children
React.ReactNode-

CardContent

PropTypeDefault
className
string-
children
React.ReactNode-

CardFooter

PropTypeDefault
className
string-
children
React.ReactNode-

CardImage

PropTypeDefault
src
string-
alt
string-
className
string-
children
React.ReactNode-

CardIcon

PropTypeDefault
className
string-
children
React.ReactNode-