ScaleRocket/Mobile

Avatar

Avatar component with initials fallback, image support, and multiple sizes.

Avatar

The Avatar component displays a user's profile picture with an automatic initials fallback when no image is available.

Basic Usage

import { Avatar } from "@/components/ui/Avatar";

// With image
<Avatar source={{ uri: user.avatarUrl }} name={user.name} />

// Without image (shows initials)
<Avatar name="John Doe" />

Sizes

Four built-in sizes:

<Avatar name="John Doe" size="xs" />  {/* 24x24 */}
<Avatar name="John Doe" size="sm" />  {/* 32x32 */}
<Avatar name="John Doe" size="md" />  {/* 48x48 */}
<Avatar name="John Doe" size="lg" />  {/* 64x64 */}
SizeDimensionsFont SizeUse Case
xs24x2410Inline mentions
sm32x3212List items
md48x4818Cards, headers
lg64x6424Profile screens

Initials Fallback

When no source is provided, the component extracts initials from the name:

function getInitials(name: string): string {
  const parts = name.trim().split(" ");
  if (parts.length === 1) return parts[0][0].toUpperCase();
  return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
}

Examples:

  • "John Doe" renders JD
  • "Alice" renders A
  • "John Michael Smith" renders JS

Background Colors

Initials avatars get a deterministic background color based on the name:

const COLORS = [
  "#3B82F6", "#EF4444", "#10B981", "#F59E0B",
  "#8B5CF6", "#EC4899", "#14B8A6", "#F97316",
];

function getColor(name: string): string {
  let hash = 0;
  for (let i = 0; i < name.length; i++) {
    hash = name.charCodeAt(i) + ((hash << 5) - hash);
  }
  return COLORS[Math.abs(hash) % COLORS.length];
}

This ensures the same user always gets the same color.

Image Loading

The component handles image loading states:

import { useState } from "react";
import { Image, View, Text } from "react-native";

function Avatar({ source, name, size = "md" }: AvatarProps) {
  const [imageError, setImageError] = useState(false);
  const dimensions = SIZES[size];

  if (source && !imageError) {
    return (
      <Image
        source={source}
        style={{
          width: dimensions,
          height: dimensions,
          borderRadius: dimensions / 2,
        }}
        onError={() => setImageError(true)}
      />
    );
  }

  return (
    <View
      style={{
        width: dimensions,
        height: dimensions,
        borderRadius: dimensions / 2,
        backgroundColor: getColor(name),
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      <Text style={{ color: "white", fontSize: FONT_SIZES[size] }}>
        {getInitials(name)}
      </Text>
    </View>
  );
}

Avatar Group

Display multiple avatars in a stacked row:

function AvatarGroup({ users, max = 3 }: { users: User[]; max?: number }) {
  const visible = users.slice(0, max);
  const remaining = users.length - max;

  return (
    <View style={{ flexDirection: "row" }}>
      {visible.map((user, i) => (
        <View key={user.id} style={{ marginLeft: i > 0 ? -8 : 0, zIndex: max - i }}>
          <Avatar source={{ uri: user.avatarUrl }} name={user.name} size="sm" />
        </View>
      ))}
      {remaining > 0 && (
        <View style={styles.overflow}>
          <Text style={styles.overflowText}>+{remaining}</Text>
        </View>
      )}
    </View>
  );
}

With Badge

Add an online indicator or notification badge:

function AvatarWithBadge({ user, isOnline }: Props) {
  return (
    <View>
      <Avatar source={{ uri: user.avatarUrl }} name={user.name} size="md" />
      {isOnline && (
        <View
          style={{
            position: "absolute",
            bottom: 0,
            right: 0,
            width: 14,
            height: 14,
            borderRadius: 7,
            backgroundColor: "#10B981",
            borderWidth: 2,
            borderColor: "white",
          }}
        />
      )}
    </View>
  );
}

Component Props

PropTypeDefaultDescription
namestringrequiredUser's display name
sourceImageSourceImage source (uri)
size"xs" | "sm" | "md" | "lg""md"Avatar size
styleViewStyleContainer style override

On this page