Dependencies
tailwindcssshadcn/uilucide-react
shadcn/ui components
npx shadcn@latest add cardnpx shadcn@latest add avatarnpx shadcn@latest add badgenpx shadcn@latest add buttonnpx shadcn@latest add separator

React profile card components built with shadcn/ui and Tailwind CSS. Five patterns — basic profile with cover image, team member grid, horizontal list layout, stats with activity chart, and an inline editable profile.

Key Classes Reference

ComponentPurpose
Avatar + AvatarFallbackProfile image with initials fallback
AvatarImageShows photo when src resolves
BadgeRole, department or achievement label
SeparatorDivider between sections
-mt-10Pull avatar up over cover image
border-4 border-whiteWhite ring around avatar on coloured cover

1. Basic Profile Card

Standard user profile card with avatar, bio and social stats.

tsx
import { Card, CardContent } from "@/components/ui/card"
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { MapPin, Link2 } from "lucide-react"

export default function BasicProfileCard() {
  return (
    <div className="p-6 flex justify-center">
      <Card className="w-72 border-0 shadow-lg overflow-hidden">
        {/* Cover */}
        <div className="h-24 bg-gradient-to-r from-red-500 to-orange-400" />
        <CardContent className="px-5 pb-5">
          {/* Avatar */}
          <div className="-mt-10 mb-3">
            <Avatar className="h-20 w-20 border-4 border-white shadow">
              <AvatarImage src="https://github.com/shadcn.png" />
              <AvatarFallback className="bg-red-500 text-white text-xl font-bold">GS</AvatarFallback>
            </Avatar>
          </div>
          {/* Info */}
          <div className="mb-3">
            <div className="flex items-center gap-2 mb-0.5">
              <h3 className="font-bold text-lg leading-none">Gagan Singh</h3>
              <Badge className="bg-red-500 text-white text-xs h-4">Pro</Badge>
            </div>
            <p className="text-sm text-muted-foreground">Founder @ LettStart Design</p>
            <div className="flex items-center gap-1 text-xs text-muted-foreground mt-1">
              <MapPin className="h-3 w-3" /> Faridabad, India
            </div>
          </div>
          <p className="text-sm text-gray-600 mb-4">
            Building Bootstrap 5 + Angular admin templates since 2021. Shipping UI components at BootstrapPlanet.
          </p>
          <Separator className="mb-4" />
          {/* Stats */}
          <div className="grid grid-cols-3 text-center mb-4">
            {[["48", "Templates"], ["12K", "Downloads"], ["4.9", "Rating"]].map(([val, label]) => (
              <div key={label}>
                <p className="font-bold text-base">{val}</p>
                <p className="text-xs text-muted-foreground">{label}</p>
              </div>
            ))}
          </div>
          <div className="flex gap-2">
            <Button className="flex-1 bg-red-500 hover:bg-red-600 text-white text-sm">Follow</Button>
            <Button variant="outline" size="icon" className="shrink-0">
              <Link2 className="h-4 w-4" />
            </Button>
          </div>
        </CardContent>
      </Card>
    </div>
  )
}

2. Team Member Cards

Grid of team member cards with role and social links.

tsx
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Twitter, Linkedin, Github } from "lucide-react"

const team = [
  { name: "Gagan Singh",   role: "Founder",           dept: "Engineering", color: "#fd4766", initials: "GS" },
  { name: "Priya Sharma",  role: "Lead Designer",     dept: "Design",      color: "#8b5cf6", initials: "PS" },
  { name: "Rahul Kumar",   role: "Frontend Dev",      dept: "Engineering", color: "#0ea5e9", initials: "RK" },
  { name: "Anita Verma",   role: "Product Manager",   dept: "Product",     color: "#10b981", initials: "AV" },
]

export default function TeamCards() {
  return (
    <div className="p-6 grid grid-cols-2 gap-4">
      {team.map((member) => (
        <div key={member.name}
          className="rounded-2xl border border-gray-100 bg-white p-5 shadow-sm hover:shadow-md transition-shadow">
          <div className="flex items-start gap-3 mb-3">
            <Avatar className="h-12 w-12 shrink-0">
              <AvatarFallback className="text-white font-bold text-sm"
                style={{ background: member.color }}>
                {member.initials}
              </AvatarFallback>
            </Avatar>
            <div className="min-w-0">
              <p className="font-semibold text-sm truncate">{member.name}</p>
              <p className="text-xs text-muted-foreground">{member.role}</p>
              <Badge variant="outline" className="text-xs mt-1 h-4">{member.dept}</Badge>
            </div>
          </div>
          <div className="flex gap-2">
            {[Twitter, Linkedin, Github].map((Icon, i) => (
              <button key={i}
                className="w-7 h-7 rounded-lg bg-gray-50 hover:bg-gray-100 flex items-center justify-center transition-colors">
                <Icon className="h-3.5 w-3.5 text-gray-500" />
              </button>
            ))}
          </div>
        </div>
      ))}
    </div>
  )
}

3. Horizontal Profile Card

Wide layout profile card for list views and dashboards.

tsx
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Mail, Phone, MoreHorizontal } from "lucide-react"

const users = [
  { name: "Gagan Singh",  role: "Admin",    email: "gagan@lettstartdesign.com", phone: "+91 98765 43210", initials: "GS", color: "#fd4766", active: true  },
  { name: "Priya Sharma", role: "Designer", email: "priya@company.com",         phone: "+91 87654 32109", initials: "PS", color: "#8b5cf6", active: true  },
  { name: "Rahul Kumar",  role: "Dev",      email: "rahul@company.com",         phone: "+91 76543 21098", initials: "RK", color: "#0ea5e9", active: false },
]

export default function HorizontalProfileCards() {
  return (
    <div className="p-6 space-y-3">
      {users.map((user) => (
        <div key={user.name}
          className="flex items-center gap-4 p-4 rounded-2xl border border-gray-100 bg-white shadow-sm hover:shadow-md transition-shadow">
          <Avatar className="h-11 w-11 shrink-0">
            <AvatarFallback className="text-white font-bold text-sm"
              style={{ background: user.color }}>
              {user.initials}
            </AvatarFallback>
          </Avatar>
          <div className="flex-1 min-w-0">
            <div className="flex items-center gap-2">
              <p className="font-semibold text-sm">{user.name}</p>
              <Badge variant="outline" className="text-xs h-4">{user.role}</Badge>
              <span className={`w-2 h-2 rounded-full ml-auto ${user.active ? "bg-green-500" : "bg-gray-300"}`} />
            </div>
            <div className="flex items-center gap-4 mt-1">
              <span className="flex items-center gap-1 text-xs text-muted-foreground">
                <Mail className="h-3 w-3" />{user.email}
              </span>
              <span className="flex items-center gap-1 text-xs text-muted-foreground hidden sm:flex">
                <Phone className="h-3 w-3" />{user.phone}
              </span>
            </div>
          </div>
          <Button variant="ghost" size="icon" className="shrink-0 h-8 w-8">
            <MoreHorizontal className="h-4 w-4" />
          </Button>
        </div>
      ))}
    </div>
  )
}

4. Profile Stats Card

Profile card with activity stats bar chart and achievement badges.

tsx
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"

const activity = [40, 65, 30, 80, 55, 90, 45, 70, 85, 60, 75, 95, 50, 88]
const achievements = ["🏆 Top Seller", "⭐ 5.0 Rating", "🚀 100+ Sales", "💎 Pro"]

export default function ProfileStatsCard() {
  const max = Math.max(...activity)

  return (
    <div className="p-6 flex justify-center">
      <div className="w-80 rounded-2xl border-0 shadow-lg overflow-hidden bg-white">
        <div className="p-6 pb-4">
          <div className="flex items-center gap-4 mb-4">
            <Avatar className="h-14 w-14">
              <AvatarFallback className="bg-red-500 text-white font-bold text-lg">GS</AvatarFallback>
            </Avatar>
            <div>
              <h3 className="font-bold text-base">Gagan Singh</h3>
              <p className="text-sm text-muted-foreground">Founder, LettStart Design</p>
            </div>
          </div>
          {/* Activity Chart */}
          <div className="mb-4">
            <p className="text-xs font-medium text-muted-foreground mb-2">14-Day Activity</p>
            <div className="flex items-end gap-1 h-14">
              {activity.map((val, i) => (
                <div
                  key={i}
                  className="flex-1 rounded-sm transition-all"
                  style={{
                    height: `${(val / max) * 100}%`,
                    background: i === activity.length - 1 ? "#fd4766" : "#fee2e2",
                  }}
                />
              ))}
            </div>
          </div>
          {/* Stats Row */}
          <div className="grid grid-cols-3 text-center border rounded-xl overflow-hidden">
            {[["48", "Templates"], ["12K", "Downloads"], ["4.9★", "Rating"]].map(([val, label], i) => (
              <div key={label} className={`py-3 ${i < 2 ? "border-r" : ""}`}>
                <p className="font-bold text-sm text-red-500">{val}</p>
                <p className="text-xs text-muted-foreground">{label}</p>
              </div>
            ))}
          </div>
        </div>
        {/* Achievements */}
        <div className="px-6 pb-5">
          <p className="text-xs font-medium text-muted-foreground mb-2">Achievements</p>
          <div className="flex flex-wrap gap-1.5">
            {achievements.map((a) => (
              <Badge key={a} variant="secondary" className="text-xs">{a}</Badge>
            ))}
          </div>
        </div>
      </div>
    </div>
  )
}

5. Editable Profile Card

Profile card with inline edit mode toggled by a button.

tsx
import { useState } from "react"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Pencil, Check, X } from "lucide-react"

interface Profile { name: string; role: string; bio: string; location: string }

export default function EditableProfileCard() {
  const [editing, setEditing] = useState(false)
  const [profile, setProfile] = useState<Profile>({
    name: "Gagan Singh",
    role: "Founder @ LettStart Design",
    bio: "Building Bootstrap 5 + Angular admin templates. Shipping at BootstrapPlanet.",
    location: "Faridabad, India",
  })
  const [draft, setDraft] = useState(profile)

  function save() { setProfile(draft); setEditing(false) }
  function cancel() { setDraft(profile); setEditing(false) }

  return (
    <div className="p-6 flex justify-center">
      <div className="w-80 rounded-2xl border shadow-sm bg-white p-6">
        <div className="flex justify-between items-start mb-4">
          <Avatar className="h-14 w-14">
            <AvatarFallback className="bg-red-500 text-white font-bold text-lg">GS</AvatarFallback>
          </Avatar>
          {!editing ? (
            <Button variant="ghost" size="icon" onClick={() => setEditing(true)} className="h-8 w-8">
              <Pencil className="h-4 w-4" />
            </Button>
          ) : (
            <div className="flex gap-1">
              <Button variant="ghost" size="icon" onClick={cancel} className="h-8 w-8 text-gray-500">
                <X className="h-4 w-4" />
              </Button>
              <Button size="icon" onClick={save} className="h-8 w-8 bg-red-500 hover:bg-red-600 text-white">
                <Check className="h-4 w-4" />
              </Button>
            </div>
          )}
        </div>

        {editing ? (
          <div className="space-y-3">
            <div>
              <label className="text-xs text-muted-foreground">Name</label>
              <Input value={draft.name} onChange={e => setDraft({ ...draft, name: e.target.value })} className="mt-1 h-8 text-sm" />
            </div>
            <div>
              <label className="text-xs text-muted-foreground">Role</label>
              <Input value={draft.role} onChange={e => setDraft({ ...draft, role: e.target.value })} className="mt-1 h-8 text-sm" />
            </div>
            <div>
              <label className="text-xs text-muted-foreground">Bio</label>
              <textarea
                value={draft.bio}
                onChange={e => setDraft({ ...draft, bio: e.target.value })}
                rows={3}
                className="w-full mt-1 text-sm border rounded-lg px-3 py-2 outline-none focus:border-red-400 resize-none"
              />
            </div>
            <div>
              <label className="text-xs text-muted-foreground">Location</label>
              <Input value={draft.location} onChange={e => setDraft({ ...draft, location: e.target.value })} className="mt-1 h-8 text-sm" />
            </div>
          </div>
        ) : (
          <>
            <h3 className="font-bold text-base">{profile.name}</h3>
            <p className="text-sm text-muted-foreground mb-2">{profile.role}</p>
            <p className="text-sm text-gray-600 mb-2">{profile.bio}</p>
            <p className="text-xs text-muted-foreground">📍 {profile.location}</p>
          </>
        )}
      </div>
    </div>
  )
}

Frequently Asked Questions

Use shadcn/ui's Card as the container, Avatar for the profile image with a fallback showing initials, Badge for the role label, and Button for action CTAs. Use flex or grid for the stats row and Separator between sections.
Split the name by spaces, take the first letter of each word, join them and uppercase: name.split(' ').map(n => n[0]).join('').toUpperCase(). Slice to two characters for a clean two-letter monogram.

Need a Full React + Next.js Dashboard Template?

Get a complete React + Next.js dashboard with 50+ components — built by the same team behind BootstrapPlanet.

Browse Templates →

Use code FIRST30 for 30% off.

Related Components