mdxui

Profile

User profile view component

Profile

The Profile view displays and manages user profile information including personal details, avatar, bio, activity history, and user statistics. It supports both viewing and editing modes.

Type Signature

interface ProfileViewProps {
  /** Page title */
  title?: string
  /** Page description */
  description?: string
  /** View layout mode */
  layout?: 'single' | 'column' | 'canvas' | 'vibe'
  /** User profile data */
  user?: {
    id: string
    name: string
    email: string
    avatar?: string
    bio?: string
    role?: string
    metadata?: Record<string, any>
  }
  /** Editable mode */
  editable?: boolean
  /** Show activity feed */
  showActivity?: boolean
  /** Show stats/metrics */
  showStats?: boolean
  /** Page-level action buttons */
  actions?: Array<{
    id: string
    label: string
    icon?: string
    onClick?: () => void
    variant?: 'default' | 'primary' | 'destructive'
  }>
  /** Loading state */
  loading?: boolean
  /** Error state */
  error?: string
  /** Child content */
  children?: React.ReactNode
}

Usage

<App name="Acme Dashboard">
  <Shell>
    <ProfileView
      user={{
        id: '1',
        name: 'Jane Doe',
        email: 'jane@acme.com',
        avatar: 'https://avatar.url/jane',
        bio: 'Product Designer at Acme',
        role: 'admin'
      }}
      editable={true}
      showActivity={true}
      showStats={true}
    />
  </Shell>
</App>

Props

title

The page title displayed in the header. Defaults to "Profile".

<ProfileView title="My Profile">

description

Optional page description.

<ProfileView
  title="Profile"
  description="Manage your personal information and preferences"
>

layout

Layout mode for the profile content. Defaults to 'single'.

// Single column layout (default)
<ProfileView layout="single">

// Multi-column layout
<ProfileView layout="column">

user

User profile data to display.

<ProfileView
  user={{
    id: 'user_123',
    name: 'Jane Doe',
    email: 'jane.doe@acme.com',
    avatar: 'https://avatar.url/jane',
    bio: 'Product Designer passionate about creating delightful user experiences',
    role: 'Team Lead',
    metadata: {
      location: 'San Francisco, CA',
      website: 'https://janedoe.com',
      twitter: '@janedoe',
      joinedAt: '2023-01-15'
    }
  }}
/>

editable

Enable profile editing mode. Defaults to false.

// View-only mode (default)
<ProfileView
  user={user}
  editable={false}
/>

// Editable mode
<ProfileView
  user={user}
  editable={true}
  onSave={handleSave}
/>

showActivity

Display recent activity feed. Defaults to true.

<ProfileView
  user={user}
  showActivity={true}
>
  {/* Shows recent user activity */}
</ProfileView>

showStats

Display user statistics and metrics. Defaults to true.

<ProfileView
  user={user}
  showStats={true}
>
  {/* Shows activity stats, contributions, etc. */}
</ProfileView>

actions

Page-level action buttons.

<ProfileView
  user={user}
  actions={[
    {
      id: 'edit',
      label: 'Edit Profile',
      icon: 'Edit',
      onClick: () => setEditable(true)
    },
    {
      id: 'share',
      label: 'Share',
      icon: 'Share',
      onClick: () => shareProfile()
    }
  ]}
/>

Layout Patterns

View Mode

Read-only profile display:

┌─────────────────────────────────────┐
│    [Avatar]    Jane Doe             │
│                jane@acme.com        │
│                Product Designer      │
├─────────────────────────────────────┤
│  Bio                                │
│  Product Designer passionate about  │
│  creating delightful UX...          │
├──────────────┬──────────────────────┤
│  Stats       │   Recent Activity    │
│  • 234 Posts │   • Updated profile  │
│  • 45 Likes  │   • Commented on ... │
└──────────────┴──────────────────────┘

Edit Mode

Editable profile form:

┌─────────────────────────────────────┐
│  [Avatar Upload]                    │
│  Change Avatar                      │
├─────────────────────────────────────┤
│  Name: [Jane Doe________]           │
│  Email: [jane@acme.com__]           │
│  Role: [Product Designer]           │
│  Bio: [________________]            │
│       [________________]            │
├─────────────────────────────────────┤
│  [Save Changes] [Cancel]            │
└─────────────────────────────────────┘

Common Patterns

Basic Profile Display

Simple profile with essential information:

<ProfileView
  user={{
    name: 'Jane Doe',
    email: 'jane@acme.com',
    avatar: avatarUrl,
    role: 'Product Designer'
  }}
  showActivity={false}
  showStats={false}
/>

Profile with Activity

Include activity timeline:

<ProfileView
  user={user}
  showActivity={true}
>
  <ActivityFeed
    items={[
      {
        id: '1',
        type: 'comment',
        content: 'Commented on "New Feature"',
        timestamp: '2 hours ago'
      },
      {
        id: '2',
        type: 'update',
        content: 'Updated profile picture',
        timestamp: '1 day ago'
      }
    ]}
  />
</ProfileView>

Profile with Stats

Show user metrics and statistics:

<ProfileView
  user={user}
  showStats={true}
>
  <ProfileStats
    stats={[
      { label: 'Posts', value: 234 },
      { label: 'Followers', value: 1250 },
      { label: 'Following', value: 320 }
    ]}
  />
</ProfileView>

Editable Profile

Allow users to edit their profile:

const [isEditing, setIsEditing] = useState(false)
const [profileData, setProfileData] = useState(user)

const handleSave = async (updates: UserProfile) => {
  await updateProfile(updates)
  setIsEditing(false)
}

<ProfileView
  user={profileData}
  editable={isEditing}
  onSave={handleSave}
  actions={[
    {
      id: 'edit',
      label: isEditing ? 'Cancel' : 'Edit Profile',
      onClick: () => setIsEditing(!isEditing)
    }
  ]}
/>

Profile Sections

Header Section

Avatar, name, and basic info:

<ProfileHeader
  avatar={user.avatar}
  name={user.name}
  email={user.email}
  role={user.role}
  editable={editable}
/>

Bio Section

User biography or description:

<ProfileBio
  bio={user.bio}
  editable={editable}
  onUpdate={handleBioUpdate}
/>

Stats Section

User metrics and achievements:

<ProfileStats
  stats={{
    posts: 234,
    followers: 1250,
    following: 320,
    joined: '2023-01-15'
  }}
/>

Activity Section

Recent user activity:

<ProfileActivity
  activities={recentActivity}
  limit={10}
  showMore={true}
/>

Metadata Section

Additional profile fields:

<ProfileMetadata
  metadata={{
    location: 'San Francisco, CA',
    website: 'https://janedoe.com',
    twitter: '@janedoe',
    linkedin: 'jane-doe'
  }}
  editable={editable}
/>

Avatar Management

Handle avatar uploads and changes:

const handleAvatarUpload = async (file: File) => {
  const url = await uploadAvatar(file)
  await updateProfile({ avatar: url })
}

<ProfileView
  user={user}
  editable={true}
>
  <AvatarUpload
    current={user.avatar}
    onUpload={handleAvatarUpload}
    maxSize={5 * 1024 * 1024} // 5MB
    accept="image/*"
  />
</ProfileView>

App Type Specializations

DeveloperApp Profile

Focus on technical information:

<ProfileView
  user={user}
  showStats={true}
>
  <DeveloperStats
    stats={{
      apiCalls: '1.2M',
      activeKeys: 3,
      repositories: 12
    }}
  />
  <RecentAPIActivity />
</ProfileView>

SaaSApp Profile

Show subscription and team info:

<ProfileView
  user={user}
>
  <SubscriptionBadge plan="Pro" />
  <TeamMemberships teams={userTeams} />
  <BillingInfo />
</ProfileView>

CRMApp Profile

Display sales metrics:

<ProfileView
  user={user}
  showStats={true}
>
  <SalesStats
    stats={{
      dealsWon: 45,
      totalRevenue: '$1.2M',
      winRate: '65%'
    }}
  />
  <RecentDeals />
</ProfileView>

Form Validation

Validate profile updates:

const profileSchema = z.object({
  name: z.string().min(2, 'Name must be at least 2 characters'),
  email: z.string().email('Invalid email address'),
  bio: z.string().max(500, 'Bio must be less than 500 characters').optional(),
  website: z.string().url().optional()
})

<ProfileView
  user={user}
  editable={true}
>
  <Form
    schema={profileSchema}
    onSubmit={handleSave}
    onError={handleError}
  >
    {/* Form fields */}
  </Form>
</ProfileView>

Privacy Controls

Manage profile visibility:

<ProfileView user={user} editable={true}>
  <PrivacySettings
    options={[
      {
        field: 'email',
        label: 'Email Address',
        visibility: 'private' | 'team' | 'public'
      },
      {
        field: 'bio',
        label: 'Biography',
        visibility: 'public'
      }
    ]}
  />
</ProfileView>

Responsive Behavior

Profile adapts to screen size:

Desktop (≥1024px)

┌────────────────────────────────────┐
│  [Avatar]  Jane Doe                │
│            jane@acme.com           │
├────────────┬───────────────────────┤
│   Bio      │   Recent Activity     │
│   Stats    │   • Commented on...   │
│            │   • Updated...        │
└────────────┴───────────────────────┘

Mobile (under 1024px)

┌────────────────────────┐
│     [Avatar]           │
│      Jane Doe          │
│   jane@acme.com        │
├────────────────────────┤
│      Bio               │
├────────────────────────┤
│      Stats             │
├────────────────────────┤
│  Recent Activity       │
│  • Commented on...     │
│  • Updated...          │
└────────────────────────┘

Integration with Payload CMS

Profile can auto-wire to Payload's users collection:

// Auto-fetches current user from Payload
<ProfileView collection="users">
  <ProfileContent />
</ProfileView>

// View specific user profile
<ProfileView collection="users" documentId={userId}>
  <ProfileContent />
</ProfileView>

Best Practices

Use Current User by Default

Load the authenticated user's profile:

// Good - uses current user
<ProfileView user={currentUser}>

// Bad - requires manual user ID
<ProfileView userId="123">

Validate Before Save

Always validate profile updates:

const handleSave = async (updates: UserProfile) => {
  try {
    await profileSchema.parseAsync(updates)
    await updateProfile(updates)
    toast.success('Profile updated')
  } catch (error) {
    toast.error('Invalid profile data')
  }
}

Show Loading States

Display skeletons while loading:

<ProfileView loading={isLoading}>
  {isLoading ? (
    <ProfileSkeleton />
  ) : (
    <ProfileContent user={user} />
  )}
</ProfileView>

Handle Missing Avatars

Provide fallback for missing avatars:

<Avatar
  src={user.avatar}
  fallback={user.name.charAt(0)}
  alt={user.name}
/>

Accessibility

Profile views implement accessibility features:

  • Semantic HTML - Use proper heading hierarchy
  • Image alt text - Descriptive avatar alt text
  • Form labels - All inputs properly labeled
  • Keyboard navigation - Tab through editable fields
<ProfileView
  user={user}
  aria-label="User profile"
>
  <section aria-labelledby="profile-heading">
    <h1 id="profile-heading" className="sr-only">
      Profile for {user.name}
    </h1>

    <img
      src={user.avatar}
      alt={`Profile picture for ${user.name}`}
    />

    <div aria-label="User information">
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
    </div>
  </section>
</ProfileView>

On this page