opensourceui
GitHubTwitter/X
  1. Home
  2. Components
  3. Profile
  4. Team Member Profile Grid
Back to Profile

Components / Profile

Team Member Profile Grid

Free Profile React component — TeamMemberProfileGrid. MIT licensed, copy-paste ready for Next.js and Tailwind CSS.

Responsive team grid with vertical portrait cards — section header plus name, role, bio, and profile link per member.

Preview

People behind the product

The team

  • Bidyut Kundu

    Bidyut Kundu

    Founder & engineer

    Building open-source UI for developers who care about craft, speed, and copy-paste simplicity.

  • Team member

    Team member

    Role

    Add your next hire here when the team grows.

  • Team member

    Team member

    Role

    Swap this placeholder with a real profile.

Setup

How to use

Free for personal and commercial use. No UI attribution required. Include the MIT copyright notice when copying component source into your project. Read the MIT license.

  1. 1

    Run in your terminal:

    $npm install clsx tailwind-merge lucide-react
  2. 2

    Copy lib/cn.ts below. Skip if you already have cn().

  3. 3

    Copy the code below and create components/profile/team-member-profile-grid.tsx in your project.

  4. 4

    Import and render:

    Example

    import { TeamMemberProfileGrid } from "@/components/profile/team-member-profile-grid";

    <TeamMemberProfileGrid title="The team" members={[{ name: "Bidyut Kundu", role: "Founder", bio: "...", image: "/profile-picture.png", profileHref: "/about" }]} />

lib/cn.ts

1
2
3
4
5
6
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
 
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

components/profile/team-member-profile-grid.tsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"use client";
 
import { forwardRef, type ComponentPropsWithoutRef } from "react";
 
import Link from "next/link";
 
import { cn } from "@/lib/cn";
 
export type TeamMemberProfile = Readonly<{
id?: string;
name: string;
role: string;
bio: string;
image: string;
imageAlt?: string;
profileHref?: string;
}>;
 
export type TeamMemberProfileGridProps = Readonly<
{
title?: string;
subtitle?: string;
members?: readonly TeamMemberProfile[];
profileLabel?: string;
} & ComponentPropsWithoutRef<"section">
>;
 
const DEFAULT_MEMBERS: readonly TeamMemberProfile[] = [
{
id: "bidyut-kundu",
name: "Bidyut Kundu",
role: "Founder & engineer",
bio: "Building open-source UI for developers who care about craft, speed, and copy-paste simplicity.",
image: "/profile-picture.png",
profileHref: "#",
},
{
id: "team-placeholder-1",
name: "Team member",
role: "Role",
bio: "Add your next hire here when the team grows.",
image: "/profile-picture.png",
profileHref: "#",
},
{
id: "team-placeholder-2",
name: "Team member",
role: "Role",
bio: "Swap this placeholder with a real profile.",
image: "/profile-picture.png",
profileHref: "#",
},
];
 
function MemberProfileLink({
href,
label,
className,
}: Readonly<{
href: string;
label: string;
className?: string;
}>) {
const linkClassName = cn(
"text-xs font-medium text-neutral-900 underline-offset-4 transition-colors hover:underline",
className,
);
 
if (!href || href === "#") {
return (
<button type="button" className={linkClassName}>
{label}
</button>
);
}
 
return (
<Link href={href} className={linkClassName}>
{label}
</Link>
);
}
 
// Team member profile grid — equal-height cards with compact portraits for about pages.
export const TeamMemberProfileGrid = forwardRef<
HTMLElement,
TeamMemberProfileGridProps
>(
(
{
className,
title = "The team",
subtitle = "People behind the product",
members = DEFAULT_MEMBERS,
profileLabel = "View profile",
...props
},
ref,
) => (
<section
ref={ref}
data-slot="team-member-profile-grid"
className={cn("w-full max-w-4xl font-sans", className)}
{...props}
>
<header className="mb-8 border-b border-neutral-200 pb-4">
<p className="font-mono text-[10px] font-semibold tracking-[0.2em] text-neutral-500 uppercase">
{subtitle}
</p>
<h2 className="mt-2 font-serif text-3xl leading-none tracking-tight text-neutral-950">
{title}
</h2>
</header>
 
<ul className="grid grid-cols-1 gap-6 md:grid-cols-3">
{members.map((member, index) => {
const alt = member.imageAlt ?? member.name;
const key = member.id ?? `${member.name}-${index}`;
 
return (
<li key={key} className="min-w-0">
<article className="flex h-full flex-col border border-neutral-200 bg-white p-5">
<div className="size-16 shrink-0 overflow-hidden border border-neutral-200 bg-neutral-100">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src={member.image}
alt={alt}
className="size-full object-cover"
/>
</div>
 
<h3 className="mt-4 font-serif text-xl leading-none tracking-tight text-neutral-950">
{member.name}
</h3>
<p className="mt-2 text-[10px] font-semibold tracking-[0.16em] text-neutral-500 uppercase">
{member.role}
</p>
<p className="mt-3 text-xs leading-relaxed text-neutral-600">
{member.bio}
</p>
 
{member.profileHref ? (
<div className="mt-auto border-t border-neutral-100 pt-4">
<MemberProfileLink
href={member.profileHref}
label={profileLabel}
/>
</div>
) : null}
</article>
</li>
);
})}
</ul>
</section>
),
);
 
TeamMemberProfileGrid.displayName = "TeamMemberProfileGrid";
PreviousNext

On this

page

Jump to a section on this page.

  • lib/cn.ts
  • components/profile/team-member-profile-grid.tsx

Sponsor spot · demo preview

Logo

Your brand name

Your tagline or category

A short pitch about your product or service goes here. This is a preview of how sponsor cards will look in this sidebar.

Your call to action

This slot is available. Sponsor Opensource UI and reach developers browsing components every day.