Skip to content

Tooltip

Tooltip provides contextual information overlay that appears on user interaction. It features intelligent positioning, automatic boundary detection, and supports both press and long press triggers with customizable delays and animations.

import { Tooltip } from 'rnc-theme';
<Tooltip content="This is a helpful tooltip">
<Button>
<ButtonText>Hover me</ButtonText>
</Button>
</Tooltip>
PropTypeDefaultDescription
childrenReact.ReactNode-Element that triggers the tooltip
contentReact.ReactNode-Content to display in tooltip
position'top' | 'bottom' | 'left' | 'right''top'Preferred tooltip position
visibleboolean-Controlled visibility state
onVisibilityChange(visible: boolean) => void-Callback when visibility changes
delaynumber500Delay in ms before showing tooltip
offsetnumber8Distance from target element
disabledbooleanfalseDisable tooltip interactions
hostNamestring-Portal host name for rendering
PositionDescriptionUse Case
topAbove the target elementDefault positioning for most cases
bottomBelow the target elementWhen space above is limited
leftTo the left of targetFor right-aligned elements
rightTo the right of targetFor left-aligned elements
const FormWithTooltips = () => {
return (
<VStack spacing="lg" padding="xl">
<VStack spacing="sm">
<HStack align="center" spacing="sm">
<Text>Password</Text>
<Tooltip
content="Password must be at least 8 characters long and contain both letters and numbers"
position="right"
delay={300}
>
<Icon name="info-circle" size={16} color="#666" />
</Tooltip>
</HStack>
<TextInput
placeholder="Enter password"
secureTextEntry
/>
</VStack>
<VStack spacing="sm">
<HStack align="center" spacing="sm">
<Text>Email Notifications</Text>
<Tooltip
content="We'll send you updates about your account and important announcements"
position="top"
>
<Icon name="bell" size={16} color="#666" />
</Tooltip>
</HStack>
<Switch />
</VStack>
</VStack>
);
};
const HelpTooltips = () => {
const [activeTooltip, setActiveTooltip] = useState<string | null>(null);
const toggleTooltip = (id: string) => {
setActiveTooltip(activeTooltip === id ? null : id);
};
return (
<View style={{ padding: 20 }}>
<HStack spacing="md" align="center">
<Tooltip
content="Create a new document"
visible={activeTooltip === 'new'}
onVisibilityChange={(visible) =>
setActiveTooltip(visible ? 'new' : null)
}
>
<Button
variant="ghost"
onPress={() => toggleTooltip('new')}
>
<ButtonIcon icon={<PlusIcon />} position="center" />
</Button>
</Tooltip>
<Tooltip
content="Save your current work"
visible={activeTooltip === 'save'}
onVisibilityChange={(visible) =>
setActiveTooltip(visible ? 'save' : null)
}
>
<Button
variant="ghost"
onPress={() => toggleTooltip('save')}
>
<ButtonIcon icon={<SaveIcon />} position="center" />
</Button>
</Tooltip>
<Tooltip
content="Share with others"
visible={activeTooltip === 'share'}
onVisibilityChange={(visible) =>
setActiveTooltip(visible ? 'share' : null)
}
>
<Button
variant="ghost"
onPress={() => toggleTooltip('share')}
>
<ButtonIcon icon={<ShareIcon />} position="center" />
</Button>
</Tooltip>
</HStack>
</View>
);
};
const StatusIndicators = () => {
const getStatusInfo = (status: string) => {
switch (status) {
case 'online':
return {
color: '#22c55e',
tooltip: 'User is currently online and active'
};
case 'away':
return {
color: '#f59e0b',
tooltip: 'User is away from keyboard'
};
case 'busy':
return {
color: '#ef4444',
tooltip: 'User is busy and may not respond immediately'
};
case 'offline':
return {
color: '#6b7280',
tooltip: 'User is offline - last seen 2 hours ago'
};
default:
return {
color: '#6b7280',
tooltip: 'Unknown status'
};
}
};
return (
<VStack spacing="md">
{['online', 'away', 'busy', 'offline'].map((status) => {
const info = getStatusInfo(status);
return (
<HStack key={status} align="center" spacing="md">
<Tooltip
content={info.tooltip}
position="right"
delay={200}
>
<View
style={{
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: info.color
}}
/>
</Tooltip>
<Text style={{ textTransform: 'capitalize' }}>
{status}
</Text>
</HStack>
);
})}
</VStack>
);
};
const ChartTooltips = () => {
const chartData = [
{ label: 'Jan', value: 45, trend: '+5%' },
{ label: 'Feb', value: 52, trend: '+15%' },
{ label: 'Mar', value: 48, trend: '-8%' },
{ label: 'Apr', value: 61, trend: '+27%' }
];
return (
<HStack spacing="lg" justify="space-around" padding="xl">
{chartData.map((data, index) => (
<Tooltip
key={data.label}
content={
<VStack align="center" spacing="xs">
<Text style={{ color: 'white', fontWeight: 'bold' }}>
{data.label} 2024
</Text>
<Text style={{ color: 'white' }}>
Value: {data.value}
</Text>
<Text style={{
color: data.trend.startsWith('+') ? '#22c55e' : '#ef4444'
}}>
{data.trend} from last month
</Text>
</VStack>
}
position={index < 2 ? 'top' : 'bottom'}
delay={100}
>
<VStack align="center" spacing="xs">
<View
style={{
width: 40,
height: data.value * 2,
backgroundColor: '#3b82f6',
borderRadius: 4
}}
/>
<Text style={{ fontSize: 12 }}>
{data.label}
</Text>
</VStack>
</Tooltip>
))}
</HStack>
);
};
const OnboardingTour = () => {
const [currentStep, setCurrentStep] = useState(0);
const [showTour, setShowTour] = useState(true);
const tourSteps = [
{
target: 'search',
content: 'Use the search bar to find anything quickly',
position: 'bottom' as const
},
{
target: 'menu',
content: 'Access all features from the main menu',
position: 'right' as const
},
{
target: 'profile',
content: 'Manage your account settings here',
position: 'left' as const
}
];
const nextStep = () => {
if (currentStep < tourSteps.length - 1) {
setCurrentStep(currentStep + 1);
} else {
setShowTour(false);
}
};
return (
<View style={{ flex: 1 }}>
{/* Header */}
<HStack justify="space-between" align="center" padding="md">
<Tooltip
content={
<VStack spacing="sm">
<Text style={{ color: 'white' }}>
{tourSteps[0].content}
</Text>
<Button size="sm" onPress={nextStep}>
<ButtonText>Next</ButtonText>
</Button>
</VStack>
}
visible={showTour && currentStep === 0}
position={tourSteps[0].position}
>
<TextInput placeholder="Search..." />
</Tooltip>
<Tooltip
content={
<VStack spacing="sm">
<Text style={{ color: 'white' }}>
{tourSteps[1].content}
</Text>
<Button size="sm" onPress={nextStep}>
<ButtonText>Next</ButtonText>
</Button>
</VStack>
}
visible={showTour && currentStep === 1}
position={tourSteps[1].position}
>
<Button variant="ghost">
<ButtonIcon icon={<MenuIcon />} position="center" />
</Button>
</Tooltip>
<Tooltip
content={
<VStack spacing="sm">
<Text style={{ color: 'white' }}>
{tourSteps[2].content}
</Text>
<Button size="sm" onPress={nextStep}>
<ButtonText>Finish Tour</ButtonText>
</Button>
</VStack>
}
visible={showTour && currentStep === 2}
position={tourSteps[2].position}
>
<Button variant="ghost">
<ButtonIcon icon={<UserIcon />} position="center" />
</Button>
</Tooltip>
</HStack>
{/* Content */}
<View style={{ flex: 1, padding: 20 }}>
<Text>Main content area</Text>
{!showTour && (
<Button onPress={() => setShowTour(true)}>
<ButtonText>Restart Tour</ButtonText>
</Button>
)}
</View>
</View>
);
};
const CustomPortalTooltip = () => {
return (
<Portal.Host name="custom-tooltips">
<View style={{ flex: 1 }}>
<Tooltip
content="This tooltip renders in a custom portal"
hostName="custom-tooltips"
position="top"
>
<Button>
<ButtonText>Custom Portal Tooltip</ButtonText>
</Button>
</Tooltip>
</View>
</Portal.Host>
);
};
const OptimizedTooltipList = React.memo(({ items }) => {
const [visibleTooltip, setVisibleTooltip] = useState<string | null>(null);
const handleTooltipChange = useCallback((id: string, visible: boolean) => {
setVisibleTooltip(visible ? id : null);
}, []);
return (
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Tooltip
content={item.description}
visible={visibleTooltip === item.id}
onVisibilityChange={(visible) =>
handleTooltipChange(item.id, visible)
}
delay={300}
>
<View style={{ padding: 10 }}>
<Text>{item.title}</Text>
</View>
</Tooltip>
)}
/>
);
});
const ConditionalTooltip = ({
children,
showTooltip,
tooltipContent,
fallbackContent
}) => {
if (!showTooltip) {
return children;
}
return (
<Tooltip
content={tooltipContent || fallbackContent}
position="top"
delay={200}
>
{children}
</Tooltip>
);
};
// Usage
<ConditionalTooltip
showTooltip={user.isNewUser}
tooltipContent="Welcome! Click here to get started"
fallbackContent="Standard action button"
>
<Button>
<ButtonText>Get Started</ButtonText>
</Button>
</ConditionalTooltip>
<Tooltip
content="Long press to see tooltip"
delay={500}
>
<Button>
<ButtonText>Long Press Me</ButtonText>
</Button>
</Tooltip>

Content Guidelines

  • Keep tooltip content concise and informative
  • Use sentence case for tooltip text
  • Avoid duplicating visible information
  • Provide actionable context when possible

Positioning Strategy

  • Choose positions that don’t obscure important content
  • Consider screen boundaries and device orientation
  • Use consistent positioning for similar elements
  • Test tooltips on different screen sizes

Interaction Design

  • Set appropriate delays based on content urgency
  • Use shorter delays for critical information
  • Implement proper cleanup to prevent memory leaks
  • Consider accessibility for users with different interaction patterns

Performance

  • Use controlled visibility for frequently updated tooltips
  • Implement proper memoization for tooltip-heavy lists
  • Clean up timeouts and references appropriately
  • Consider portal hosts for complex layouts