Profile Page React block for Tailwind CSS v4
v1.0.0Added in 1.14.0
A React user profile screen: cover band, avatar with presence, bio and links, follow and message, counters, tabs and an edit profile sheet.
The header shows the cover band, the avatar with a presence dot (online, away, busy, or "Seen 2 hours ago" while offline), the name with a verified mark, the handle, a headline, the bio, the location, links that open in a new tab and the join date. Follow flips to Following at once with a popping check and adds one to Followers; if onFollow rejects it flips back and a screen reader hears why. Message calls onMessage. The Projects counter opens the Projects tab; Followers and Following are buttons when onStatClick is set. Three tabs (arrow keys move between them, Home and End jump): Overview with the pinned project cards, a year of contributions as a heatmap (hover or focus a day for its count) and the latest activity with View all; Projects with every project as a link card (language, stars, last update, a Private badge); Activity as a timeline grouped by day. On the owner's profile Edit profile opens a sheet from the right (a bottom sheet with a drag handle on phones): change the photo (crop, upload progress, remove), the name, the username, a bio with a 160 character counter, the location and the website. Save checks the fields first: a missing name, a username that is not 3 to 20 letters, numbers or underscores, or a website that is not an address turn red and the first one takes focus; the messages then follow your typing. While onSave runs, Save shows a spinner and the sheet stays open; a rejection keeps the draft and shows its message, a success updates the header and closes the sheet. Escape or Cancel closes without saving. On phones the header stacks, the counters become a row of three tiles, the actions fill the width, and the tab pages follow a horizontal swipe.
Profile Page is a Pro React block in the React blocks category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add profile-page, together with Avatar, Avatar Upload, Badge, Button, Card and 14 more and the npm packages motion and lucide-react.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add profile-pageProfile Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import ProfilePage from @/components/blocks/profile-page.
// app/(app)/u/[handle]/page.tsx
"use client"
import { ProfilePage } from "@/components/blocks/profile-page"
import { PROFILE_PAGE_SAMPLE } from "@/components/blocks/profile-page-data"
export default function ProfileRoute() {
return (
<ProfilePage
user={PROFILE_PAGE_SAMPLE.user}
projects={PROFILE_PAGE_SAMPLE.projects}
isOwner
onFollow={async (following) => {
await fetch("/api/follow", { method: following ? "POST" : "DELETE" })
}}
onSave={async (values) => {
const res = await fetch("/api/profile", { method: "PUT", body: JSON.stringify(values) })
if (!res.ok) throw new Error("We could not save your profile. Try again.")
}}
/>
)
}Props
userdefault"PROFILE_PAGE_SAMPLE.user"ProfileUserThe person: { id, name, handle, avatar?, cover?, coverColor?, headline?, bio?, location?, website?, links?, joinedAt?, status?, lastSeen?, verified?, followers, following, projectCount?, isFollowing? }. Default: Emma Carter (PROFILE_PAGE_SAMPLE.user).
projectsdefault"PROFILE_PAGE_SAMPLE.projects"ProfileProject[]Project cards: { id, name, description?, href?, language?, languageColor?, stars?, updatedAt?, pinned?, visibility? }. Default: six sample projects.
activitydefault"PROFILE_PAGE_SAMPLE.activity"ProfileActivity[]Timeline rows: { id, type: commit | release | comment | star | project | follow | review, title, description?, time, href? }.
contributionsdefault"PROFILE_PAGE_SAMPLE.contributions"ProfileContribution[]A year of { date, count, level } days for the heatmap, starting on a Sunday; [] hides it. Default: a seeded sample year.
isOwnerdefaultfalsebooleanThe viewer's own profile: Edit profile replaces Follow and Message.
tab"overview" | "projects" | "activity"The open tab, controlled; pair it with onTabChange (a route per tab).
defaultTabdefault"overview""overview" | "projects" | "activity"The tab open on mount.
onTabChange(tab: ProfileTab) => voidCalled when another tab opens, by a press, the keyboard, a swipe or the Projects counter.
followingbooleanWhether the viewer follows this person, controlled; pair it with onFollow.
defaultFollowingdefaultfalsebooleanFollowing on mount while uncontrolled; user.isFollowing turns it on too.
onFollow(following: boolean) => void | Promise<unknown>Runs after Follow flips (optimistic); a rejection flips it back and announces labels.followFailed.
onMessage() => voidAdds a Message button next to Follow.
onStatClick(stat: "followers" | "following") => voidTurns the Followers and Following counters into buttons (open a list, a route). The Projects counter always opens the Projects tab.
editOpenbooleanThe edit sheet, controlled; pair it with onEditOpenChange. Every opening starts from the current profile.
defaultEditOpendefaultfalsebooleanOpens the edit sheet on mount.
onEditOpenChange(open: boolean) => voidCalled when the edit sheet opens or closes.
onSave(values: ProfileDraft) => void | Promise<unknown>Saves { name, handle, bio, location, website, avatar } after the checks pass (website with https:// added). A promise keeps Save loading; a rejection keeps the sheet open with its message.
onAvatarUpload(blob: Blob, context: { file; signal; onProgress }) => Promise<string | void>Uploads the cropped photo from the edit sheet; resolve the new url. Without it the photo is a local preview until Save.
pinnedCountdefault4numberProject cards on Overview: pinned ones first, then the rest in order; 0 hides the section.
recentCountdefault3numberActivity rows on Overview, newest first; 0 hides the section.
showHeatmapdefaulttruebooleanThe contributions heatmap on Overview.
heatmapColordefault"#39d353"stringAny CSS color for the heatmap cells; busier days are more opaque.
swipeabledefaulttruebooleanBelow 768px the tab pages follow a horizontal swipe.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Follow, Save and the tab indicator.
radiusdefault"md""sm" | "md" | "lg"Corner size of the header, the cards and the panels (12 / 16 / 24px).
loadingdefaultfalsebooleanSkeletons in the real layout while the profile loads.
animateIndefaulttruebooleanThe header parts rise in one after another when the page mounts on the client; never while hydrating or under reduced motion.
localedefault""stringIntl locale for the counters, the join date and relative times. Empty falls back to the UIProvider locale, then en-US (the demo offers en-GB, de-DE and ro-RO).
nowDate | numberThe reference time for "Updated 5 hours ago", "Seen 2 hours ago" and the activity times; default the live clock.
labelsPartial<ProfilePageLabels>Every visible string, merged over the English DEFAULT_PROFILE_PAGE_LABELS; avatarUpload carries the photo picker's strings.
classNamestringClasses for the root.
classNamesPartial<Record<"header" | "cover" | "avatar" | "identity" | "name" | "bio" | "meta" | "stats" | "stat" | "actions" | "tabs" | "overview" | "projects" | "activity" | "projectCard" | "heatmap" | "timeline" | "sheet", string>>Classes for the inner parts.
Parts
DEFAULT_PROFILE_PAGE_LABELS
constantEvery English string of the page. Spread it and override what you translate.
PROFILE_PAGE_SAMPLE
constantThe demo data the page renders without props: { user, projects, activity, contributions }.
PROFILE_SAMPLE_USER
constantEmma Carter, a design engineer in Brooklyn, New York, online, with 1,284 followers.
PROFILE_SAMPLE_PROJECTS
constantSix projects, four of them pinned, one private.
PROFILE_SAMPLE_ACTIVITY
constantSeven activity rows: a release, commits, a review, a star, a comment, a new project and a follow.
PROFILE_SAMPLE_CONTRIBUTIONS
constantA seeded year of contribution days ending on PROFILE_PAGE_NOW.
PROFILE_PAGE_NOW
constantThe fixed clock of the sample (Nov 5, 2026, 2:40 PM UTC); pass it as now so screenshots never drift.
generateContributions
functionSeeded demo contribution days, the same on every render. For demos and tests; pass your real calendar in production.
enddefault"PROFILE_PAGE_NOW"Date | numberThe last day.
weeksdefault53numberHow many weeks back, rounded to a Sunday.
seeddefault2026numberThe random seed; the same seed gives the same days.
returnsProfileContribution[]The days, oldest first.
ProfileUser
typeThe person shown in the header.
ProfileProject
typeOne project card.
ProfileActivity
typeOne activity row.
ProfileContribution
typeOne heatmap day: { date: "2026-11-05", count, level: 0 to 4 }.
ProfileDraft
typeWhat onSave receives: { name, handle, bio, location, website, avatar }.
ProfilePageLabels
typeThe shape of labels.
Examples
Your own profile
isOwner swaps Follow for Edit profile; save to see the header update.
Edit sheet open
Photo, name, username, bio, location and website, checked on Save. A bottom sheet on phones.
Save that fails
onSave rejects once: the sheet stays open with the message and the draft.
Offline, photo cover, brand
A cover photo, Seen 2 hours ago, 18.4K followers and the brand tone.
New member
No projects, activity or heatmap yet: empty states instead of blank space.
Follow that fails
Follow flips at once and flips back when onFollow rejects.
Projects tab, large corners
defaultTab="projects" with radius="lg".
Loading
Skeletons in the real layout.
Dependencies
Files
- components/blocks/profile-page.tsx
- components/blocks/profile-page-data.ts
FAQ
Is the React Profile Page block free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Profile Page?
Run npx wingo-ui@latest add profile-page. It also adds the Wingo UI items it builds on (Avatar, Avatar Upload, Badge, Button, Card and 14 more) and the npm packages motion and lucide-react. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/profile-page.
How does Profile Page behave on phones?
On the owner's profile Edit profile opens a sheet from the right (a bottom sheet with a drag handle on phones): change the photo (crop, upload progress, remove), the name, the username, a bio with a 160 character counter, the location and the website.
See also
More React blocks
Articles using this component
- Shadcn Profile Page in React: Follow, Tabs and Edit Sheet
Build it: screens and apps9 min read