Skip to content

Avatar

Avatar provides a comprehensive solution for displaying user profile images with automatic fallbacks, animated interactions, and grouping capabilities. It supports various shapes, sizes, and styling variants with built-in badge functionality.

import { Avatar, AvatarBadge, AvatarGroup } from 'rnc-theme';
<Avatar
source={{ uri: 'https://example.com/avatar.jpg' }}
fallbackText="John Doe"
/>
PropTypeDefaultDescription
sizeComponentSize'md'Avatar size (xs, sm, md, lg, xl)
variantComponentVariant'default'Visual style variant
shape'circle' | 'square''circle'Avatar shape
source{ uri: string } | number-Image source for avatar
fallbackTextstring-Text to generate initials from
fallbackIconReact.ReactNode<User />Custom fallback icon
onPress() => void-Press handler function
disabledbooleanfalseDisable avatar interactions
borderWidthnumber0Border width around avatar
borderColorkeyof Theme['colors']-Border color from theme
showBadgebooleanfalseShow status badge
badgeColorkeyof Theme['colors']'success'Badge color from theme
badgeSizenumber-Custom badge size
animatedbooleantrueEnable entry and press animations
imageStyleImageStyle-Additional image styles
textStyleTextStyle-Additional text styles
PropTypeDefaultDescription
colorkeyof Theme['colors']'success'Badge background color
sizenumber10Badge diameter
position'top-right' | 'top-left' | 'bottom-right' | 'bottom-left''top-right'Badge position
PropTypeDefaultDescription
childrenReact.ReactNode-Avatar components to group
maxnumber3Maximum avatars to show
sizeComponentSize'md'Size for all avatars in group
spacingnumber-8Spacing between avatars (negative for overlap)
showMorebooleantrueShow remaining count avatar
moreTextstring+{count}Custom text for more avatar
onMorePress() => void-Handler for more avatar press
SizeDimensionsFont SizeIcon SizeBadge Size
xs24x2410px12px6px
sm32x3212px16px8px
md40x4014px20px10px
lg56x5618px28px12px
xl72x7224px36px16px
VariantDescriptionUse Case
defaultDefault surface stylingGeneral purpose avatars
primaryPrimary theme colorImportant users, admins
secondarySecondary theme colorSecondary users
outlineTransparent with borderPlaceholder avatars
filledFilled surfaceStandard user avatars
ghostFully transparentSubtle avatar displays
successSuccess state colorOnline/active users
errorError state colorBlocked/error states
warningWarning state colorWarning states
infoInfo state colorInformation states
destructiveDestructive action colorDeleted/removed users
const UserProfile = ({ user }) => {
return (
<VStack spacing="md" align="center" padding="lg">
<Avatar
size="xl"
source={{ uri: user.avatar }}
fallbackText={user.name}
showBadge={user.isOnline}
badgeColor="success"
onPress={() => navigateToProfile(user.id)}
/>
<VStack spacing="xs" align="center">
<Text variant="heading" size="lg">{user.name}</Text>
<Text variant="body" color="muted">{user.role}</Text>
</VStack>
</VStack>
);
};
<HStack spacing="md" align="center" padding="lg">
<Avatar size="xs" fallbackText="XS" variant="primary" />
<Avatar size="sm" fallbackText="SM" variant="primary" />
<Avatar size="md" fallbackText="MD" variant="primary" />
<Avatar size="lg" fallbackText="LG" variant="primary" />
<Avatar size="xl" fallbackText="XL" variant="primary" />
</HStack>
<VStack spacing="lg" padding="lg">
{/* Circle Variants */}
<HStack spacing="md" align="center">
<Avatar variant="default" fallbackText="DE" />
<Avatar variant="primary" fallbackText="PR" />
<Avatar variant="outline" fallbackText="OU" />
<Avatar variant="success" fallbackText="SU" />
<Avatar variant="error" fallbackText="ER" />
</HStack>
{/* Square Variants */}
<HStack spacing="md" align="center">
<Avatar shape="square" variant="default" fallbackText="DE" />
<Avatar shape="square" variant="primary" fallbackText="PR" />
<Avatar shape="square" variant="outline" fallbackText="OU" />
<Avatar shape="square" variant="success" fallbackText="SU" />
<Avatar shape="square" variant="error" fallbackText="ER" />
</HStack>
</VStack>
<VStack spacing="lg" padding="lg">
<Text variant="heading" size="md">Status Indicators</Text>
<HStack spacing="md" align="center">
<Avatar
fallbackText="Online"
showBadge={true}
badgeColor="success"
badgeSize={12}
/>
<Avatar
fallbackText="Away"
showBadge={true}
badgeColor="warning"
badgeSize={12}
/>
<Avatar
fallbackText="Busy"
showBadge={true}
badgeColor="error"
badgeSize={12}
/>
<Avatar
fallbackText="Offline"
showBadge={true}
badgeColor="muted"
badgeSize={12}
/>
</HStack>
<Text variant="heading" size="md">Badge Positions</Text>
<HStack spacing="md" align="center">
<Avatar fallbackText="TR">
<AvatarBadge position="top-right" color="success" />
</Avatar>
<Avatar fallbackText="TL">
<AvatarBadge position="top-left" color="warning" />
</Avatar>
<Avatar fallbackText="BR">
<AvatarBadge position="bottom-right" color="error" />
</Avatar>
<Avatar fallbackText="BL">
<AvatarBadge position="bottom-left" color="info" />
</Avatar>
</HStack>
</VStack>
const TeamMembers = ({ team }) => {
return (
<VStack spacing="lg" padding="lg">
<Text variant="heading" size="md">Team Members</Text>
{/* Basic Group */}
<AvatarGroup max={3} size="md">
{team.members.map(member => (
<Avatar
key={member.id}
source={{ uri: member.avatar }}
fallbackText={member.name}
/>
))}
</AvatarGroup>
{/* Large Group with Custom More */}
<AvatarGroup
max={5}
size="lg"
spacing={-12}
showMore={true}
moreText={`+${team.members.length - 5} more`}
onMorePress={() => showAllMembers()}
>
{team.members.map(member => (
<Avatar
key={member.id}
source={{ uri: member.avatar }}
fallbackText={member.name}
showBadge={member.isOnline}
badgeColor="success"
/>
))}
</AvatarGroup>
{/* Compact Group */}
<AvatarGroup max={4} size="sm" spacing={-6}>
{team.members.map(member => (
<Avatar
key={member.id}
source={{ uri: member.avatar }}
fallbackText={member.name}
variant="outline"
/>
))}
</AvatarGroup>
</VStack>
);
};
const UserList = ({ users, onUserSelect, selectedUserId }) => {
return (
<VStack spacing="md" padding="lg">
<Text variant="heading">Select User</Text>
<HStack spacing="md" wrap={true}>
{users.map(user => (
<Avatar
key={user.id}
size="lg"
source={{ uri: user.avatar }}
fallbackText={user.name}
variant={selectedUserId === user.id ? 'primary' : 'outline'}
borderWidth={selectedUserId === user.id ? 3 : 1}
borderColor={selectedUserId === user.id ? 'primary' : 'border'}
showBadge={user.isActive}
badgeColor={user.isActive ? 'success' : 'muted'}
onPress={() => onUserSelect(user.id)}
animated={true}
/>
))}
</HStack>
</VStack>
);
};
const AvatarFallbacks = () => {
return (
<VStack spacing="lg" padding="lg">
<Text variant="heading">Fallback Examples</Text>
<HStack spacing="md" align="center">
{/* Image loads successfully */}
<Avatar
source={{ uri: 'https://valid-image-url.jpg' }}
fallbackText="John Doe"
fallbackIcon={<UserIcon />}
/>
{/* Image fails, shows initials */}
<Avatar
source={{ uri: 'https://invalid-image-url.jpg' }}
fallbackText="Jane Smith"
fallbackIcon={<UserIcon />}
/>
{/* No image or text, shows custom icon */}
<Avatar
fallbackIcon={<CameraIcon />}
variant="outline"
/>
{/* No props, shows default User icon */}
<Avatar variant="filled" />
</HStack>
</VStack>
);
};
const AnimatedAvatar = ({ user, onStatusChange }) => {
const [isHovered, setIsHovered] = useState(false);
const avatarRef = useRef<AvatarRef>(null);
const handlePress = () => {
// Trigger custom animation
avatarRef.current?.animate?.();
onStatusChange(user.id);
};
return (
<Pressable
onPressIn={() => setIsHovered(true)}
onPressOut={() => setIsHovered(false)}
>
<Avatar
ref={avatarRef}
size="xl"
source={{ uri: user.avatar }}
fallbackText={user.name}
variant={isHovered ? 'primary' : 'default'}
showBadge={true}
badgeColor={user.status === 'online' ? 'success' : 'muted'}
borderWidth={isHovered ? 3 : 0}
borderColor="primary"
animated={true}
onPress={handlePress}
/>
</Pressable>
);
};
const UserStatusAvatar = ({ user, connectionStatus }) => {
const getAvatarConfig = () => {
switch (connectionStatus) {
case 'connecting':
return {
variant: 'outline',
badgeColor: 'warning',
disabled: true
};
case 'connected':
return {
variant: 'default',
badgeColor: 'success',
disabled: false
};
case 'disconnected':
return {
variant: 'muted',
badgeColor: 'error',
disabled: true
};
default:
return {
variant: 'default',
badgeColor: 'muted',
disabled: false
};
}
};
const config = getAvatarConfig();
return (
<Avatar
size="lg"
source={{ uri: user.avatar }}
fallbackText={user.name}
variant={config.variant}
showBadge={true}
badgeColor={config.badgeColor}
disabled={config.disabled}
animated={!config.disabled}
onPress={() => !config.disabled && openUserProfile(user.id)}
/>
);
};
const LoadingAvatar = ({ userId, size = 'md' }) => {
const [imageLoading, setImageLoading] = useState(true);
const [imageError, setImageError] = useState(false);
const { data: user, loading } = useUser(userId);
if (loading) {
return (
<Avatar
size={size}
variant="outline"
animated={false}
>
<ActivityIndicator size="small" />
</Avatar>
);
}
return (
<Avatar
size={size}
source={user?.avatar ? { uri: user.avatar } : undefined}
fallbackText={user?.name}
variant="default"
onPress={() => navigateToProfile(userId)}
imageStyle={{
opacity: imageLoading ? 0.5 : 1
}}
/>
);
};
const ChatMessage = ({ message, sender, isCurrentUser }) => {
return (
<HStack
spacing="sm"
align="flex-start"
direction={isCurrentUser ? 'row-reverse' : 'row'}
padding="sm"
>
<Avatar
size="sm"
source={{ uri: sender.avatar }}
fallbackText={sender.name}
showBadge={sender.isOnline}
badgeColor="success"
/>
<VStack
spacing="xs"
flex={1}
align={isCurrentUser ? 'flex-end' : 'flex-start'}
>
<Text size="sm" color="muted">{sender.name}</Text>
<MessageBubble
content={message.content}
isCurrentUser={isCurrentUser}
/>
</VStack>
</HStack>
);
};

Image Handling

  • Always provide fallback text or icons for failed image loads
  • Use appropriate image sizes to avoid unnecessary bandwidth usage
  • Consider implementing image caching for better performance

Performance

  • Use React.memo for avatar lists that don’t change frequently
  • Implement lazy loading for avatar groups with many members
  • Consider virtual scrolling for very large contact lists

Status Indicators

  • Use consistent badge colors across your application
  • Position badges consistently (typically top-right)
  • Provide clear visual hierarchy for different status types