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
| Component | Purpose |
|---|---|
Avatar + AvatarFallback | Profile image with initials fallback |
AvatarImage | Shows photo when src resolves |
Badge | Role, department or achievement label |
Separator | Divider between sections |
-mt-10 | Pull avatar up over cover image |
border-4 border-white | White ring around avatar on coloured cover |
1. Basic Profile Card
Standard user profile card with avatar, bio and social stats.
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.
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.
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.
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.
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
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.