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 */}| Size | Dimensions | Font Size | Use Case |
|---|---|---|---|
xs | 24x24 | 10 | Inline mentions |
sm | 32x32 | 12 | List items |
md | 48x48 | 18 | Cards, headers |
lg | 64x64 | 24 | Profile 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
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | required | User's display name |
source | ImageSource | — | Image source (uri) |
size | "xs" | "sm" | "md" | "lg" | "md" | Avatar size |
style | ViewStyle | — | Container style override |