Skip to content

Icon Button

The IconButton component provides an interactive button that displays only an icon. It supports multiple visual variants, configurable sizes, loading states, and follows the same design patterns as the Button component.

import { IconButton } from '@space-uy/pulsar-ui';
<IconButton iconName="Heart" onPress={() => console.log('Pressed!')} />
PropertyTypeRequiredDefault valueDescription
iconNameIconName-Lucide React Native icon name to display
onPress() => void-Function executed when the button is pressed
variant'flat' | 'outline' | 'transparent' | 'destructive''flat'Visual variant of the button
size'small' | 'medium' | 'large''medium'Size of the button
loadingbooleanfalseShows loading indicator and disables the button
disabledbooleanfalseDisables the button and reduces its opacity
styleStyleProp<ViewStyle>-Custom styles for the button container
...restPressableProps-Additional Pressable component props

Solid button with primary background color.

<IconButton iconName="Heart" variant="flat" />

Button with border and transparent background.

<IconButton iconName="Share" variant="outline" />

Completely transparent button, ideal for toolbars and secondary actions.

<IconButton iconName="Settings" variant="transparent" />

Button for destructive actions with warning color.

<IconButton iconName="Trash" variant="destructive" />

Compact button (32px) for dense layouts.

<IconButton iconName="Edit" size="small" />

Standard size (40px) for most use cases.

<IconButton iconName="Save" size="medium" />

Large button (48px) for primary actions.

<IconButton iconName="Plus" size="large" />

Shows loading indicator and disables interactions.

<IconButton iconName="Download" loading={true} />

Disables the button and reduces its opacity.

<IconButton iconName="Delete" disabled={true} />
<View style={{ flexDirection: 'row', gap: 8 }}>
<IconButton iconName="Bold" variant="transparent" size="small" />
<IconButton iconName="Italic" variant="transparent" size="small" />
<IconButton iconName="Underline" variant="transparent" size="small" />
<IconButton iconName="Link" variant="transparent" size="small" />
</View>
<View
style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 16 }}
>
<IconButton
iconName="ArrowLeft"
variant="transparent"
onPress={() => navigation.goBack()}
/>
<IconButton iconName="Share" variant="outline" onPress={handleShare} />
</View>
<View style={{ flexDirection: 'row', gap: 12 }}>
<IconButton iconName="Heart" variant="outline" onPress={handleLike} />
<IconButton
iconName="MessageCircle"
variant="outline"
onPress={handleComment}
/>
<IconButton iconName="Share" variant="outline" onPress={handleShare} />
<IconButton
iconName="Bookmark"
variant="transparent"
onPress={handleBookmark}
/>
</View>
<IconButton
iconName="Plus"
variant="flat"
size="large"
style={{
position: 'absolute',
bottom: 24,
right: 24,
borderRadius: 28,
elevation: 4,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.2,
shadowRadius: 4,
}}
onPress={handleAddNew}
/>
const [isFavorite, setIsFavorite] = useState(false);
<IconButton
iconName={isFavorite ? 'Heart' : 'HeartOff'}
variant={isFavorite ? 'flat' : 'outline'}
onPress={() => setIsFavorite(!isFavorite)}
style={{
backgroundColor: isFavorite ? colors.destructive : undefined,
}}
/>;
const [isProcessing, setIsProcessing] = useState(false);
const handleProcess = async () => {
setIsProcessing(true);
try {
await processData();
} finally {
setIsProcessing(false);
}
};
<IconButton
iconName="Download"
loading={isProcessing}
onPress={handleProcess}
disabled={isProcessing}
/>;
const [selectedAction, setSelectedAction] = useState<string | null>(null);
const actions = [
{ id: 'edit', icon: 'Edit', label: 'Edit' },
{ id: 'copy', icon: 'Copy', label: 'Copy' },
{ id: 'share', icon: 'Share', label: 'Share' },
{ id: 'delete', icon: 'Trash', label: 'Delete' },
];
<View
style={{
flexDirection: 'row',
gap: 4,
padding: 4,
backgroundColor: colors.altBackground,
borderRadius: theme.roundness,
}}
>
{actions.map((action) => (
<IconButton
key={action.id}
iconName={action.icon as IconName}
variant={selectedAction === action.id ? 'flat' : 'transparent'}
size="small"
onPress={() => setSelectedAction(action.id)}
/>
))}
</View>;
<IconButton
iconName="Star"
variant="transparent"
style={{
backgroundColor: colors.primary + '20',
borderWidth: 2,
borderColor: colors.primary,
borderStyle: 'dashed',
}}
onPress={handleFavorite}
/>
const [isPlaying, setIsPlaying] = useState(false);
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 16 }}>
<IconButton
iconName="SkipBack"
variant="transparent"
onPress={handlePrevious}
/>
<IconButton
iconName={isPlaying ? 'Pause' : 'Play'}
variant="flat"
size="large"
onPress={() => setIsPlaying(!isPlaying)}
/>
<IconButton
iconName="SkipForward"
variant="transparent"
onPress={handleNext}
/>
</View>;
<View style={{ flexDirection: 'row', gap: 8 }}>
<IconButton
iconName="Search"
variant="transparent"
onPress={() => setShowSearch(true)}
/>
<IconButton
iconName="Filter"
variant="transparent"
onPress={() => setShowFilters(true)}
/>
<IconButton
iconName="MoreVertical"
variant="transparent"
onPress={() => setShowMenu(true)}
/>
</View>
const [interactions, setInteractions] = useState({
liked: false,
bookmarked: false,
shared: false,
});
<View style={{ flexDirection: 'row', gap: 12, justifyContent: 'space-around' }}>
<IconButton
iconName={interactions.liked ? 'Heart' : 'HeartOff'}
variant="transparent"
onPress={() => setInteractions((prev) => ({ ...prev, liked: !prev.liked }))}
style={{
tintColor: interactions.liked ? colors.destructive : colors.foreground,
}}
/>
<IconButton
iconName="MessageCircle"
variant="transparent"
onPress={handleComment}
/>
<IconButton
iconName={interactions.shared ? 'Share' : 'ShareOff'}
variant="transparent"
onPress={() =>
setInteractions((prev) => ({ ...prev, shared: !prev.shared }))
}
/>
<IconButton
iconName={interactions.bookmarked ? 'Bookmark' : 'BookmarkOff'}
variant="transparent"
onPress={() =>
setInteractions((prev) => ({ ...prev, bookmarked: !prev.bookmarked }))
}
/>
</View>;
  • Built on top of the ButtonContainer component for consistent behavior
  • Icon size automatically adjusts based on button size with appropriate padding
  • Loading state replaces the icon with a LoadingIndicator component
  • The button is perfectly square with equal width and height
  • Proper hit slop is applied for better touch accessibility
  • All animations and interactions follow the same patterns as regular buttons

The icon size is automatically calculated based on button size:

  • Small button (32px): Icon size 16px with 4px padding
  • Medium button (40px): Icon size 24px with 8px padding
  • Large button (48px): Icon size 32px with 8px padding
  • The icon button is fully keyboard accessible
  • Screen readers properly announce the button’s purpose based on the icon
  • Loading and disabled states prevent inappropriate interactions
  • Touch targets meet minimum size requirements for accessibility
  • Colors respect contrast ratios defined in the theme
  • Proper focus management and navigation support