DonutChart Component
A reusable, animated donut chart component for React Native with smooth spring animations and configurable gap spacing.
Features
- ✨ Smooth Animations - Spring-based animations with configurable delays
- 📊 Gap Spacing - Configurable gaps between chart segments
- 🎨 Customizable Colors - Each segment can have its own color
- 📱 Responsive - Works on all screen sizes
- 🎯 TypeScript Support - Full type safety with interfaces
- ⚡ Performance Optimized - Uses React Native Reanimated for 60fps
Usage
Basic Example
typescript
import { DonutChart } from '@/components/donut-chart';
const categories = [
{
id: 'category-1',
name: 'CATEGORY 1',
value: 100,
percentage: 50,
color: '#FF9500',
},
{
id: 'category-2',
name: 'CATEGORY 2',
value: 50,
percentage: 25,
color: '#FFCC00',
},
];
<DonutChart
categories={categories}
sizeConfig={{
size: 280,
strokeWidth: 20,
gapSize: 12,
}}
animationDelay={200}
/>With Custom Sizing
typescript
<DonutChart
categories={data}
sizeConfig={{
size: 200, // Smaller chart
strokeWidth: 15, // Thinner stroke
gapSize: 8, // Smaller gaps
}}
animationDelay={0} // No delay
disableAnimation={false}
/>Props
DonutChart Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
categories | ChartCategory[] | ✅ | - | Array of data categories |
sizeConfig | SizeConfig | ❌ | { size: 280, strokeWidth: 20, gapSize: 12 } | Chart sizing configuration |
animationDelay | number | ❌ | 0 | Delay before animation starts (ms) |
disableAnimation | boolean | ❌ | false | Disable all animations |
ChartCategory
| Property | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Unique identifier for the category |
name | string | ✅ | Display name for the category |
value | number | ✅ | Numerical value for the category |
percentage | number | ✅ | Percentage of total (0-100) |
color | string | ✅ | Hex color for chart segment |
icon | keyof typeof Ionicons.glyphMap | ❌ | Ionicons icon name (optional) |
SizeConfig
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
size | number | ❌ | 280 | Chart diameter in pixels |
strokeWidth | number | ❌ | 20 | Stroke width of chart segments |
gapSize | number | ❌ | 12 | Gap size in degrees between segments |
Chart Features
Gap Spacing
- Default Gap: 12 degrees between segments
- Configurable: Can be adjusted via
sizeConfig.gapSize - Automatic Adjustment: Segments are proportionally reduced to accommodate gaps
Animation
- Spring Physics: Uses
damping: 15, stiffness: 100for initial animation - Staggered Animation: 100ms delay between each category
- Smart Updates: Smooth transitions when data changes
- Disable Option: Can turn off animations completely
Sizing
- Diameter: Configurable via
sizeConfig.size - Stroke Width: Configurable via
sizeConfig.strokeWidth - Responsive: Automatically scales to container
Examples
You can play with gap size and animation delay to get the desired effect. You can also use the disableAnimation prop to turn off animations completely. Gap size is automatically adjusted to accommodate the gap size between segments.
Small Chart
typescript
<DonutChart
categories={smallData}
sizeConfig={{
size: 150,
strokeWidth: 12,
gapSize: 8,
}}
animationDelay={100}
/>Large Chart
typescript
<DonutChart
categories={largeData}
sizeConfig={{
size: 400,
strokeWidth: 25,
gapSize: 15,
}}
animationDelay={300}
/>Static Chart (No Animation)
typescript
<DonutChart
categories={staticData}
sizeConfig={{
size: 280,
strokeWidth: 20,
gapSize: 12,
}}
disableAnimation={true}
/>Complete Demo Example
typescript
import { DonutChart } from '@/components/charts/donut-chart';
const sampleCategories = [
{
id: 'claude-4-sonnet',
name: 'CLAUDE-4-SONNET',
value: 117.89,
percentage: 73,
color: '#FF9500',
},
{
id: 'claude-4-opus',
name: 'CLAUDE-4-OPUS',
value: 35.54,
percentage: 22,
color: '#FFCC00',
},
{
id: 'auto',
name: 'AUTO',
value: 4.93,
percentage: 3,
color: '#8E8E93',
},
];
export const DonutChartDemo = () => {
const totalValue = sampleCategories.reduce((sum, category) => sum + category.value, 0);
return (
<View style={styles.chartContainer}>
<View style={styles.chartWrapper}>
<DonutChart
categories={sampleCategories}
sizeConfig={{
size: 280,
strokeWidth: 20,
gapSize: 12,
}}
animationDelay={1000}
disableAnimation={false}
/>
<View style={styles.chartCenter}>
<Text style={styles.totalValue}>{totalValue.toFixed(2)}</Text>
<Text style={styles.totalUnit}>USD</Text>
</View>
</View>
</View>
);
};Dependencies
- react-native-reanimated: For smooth animations
- react-native-svg: For SVG chart rendering
- @expo/vector-icons: For optional icon support
Performance
- 60fps Animations: Uses shared values for optimal performance
- Efficient Rendering: SVG-based with minimal re-renders
- Memory Management: Proper cleanup of animated values
- Optimized Calculations: Pre-calculated values for smooth updates
Customization
Custom Gap Size
To change the gap between segments, use the sizeConfig.gapSize prop:
typescript
<DonutChart
categories={data}
sizeConfig={{
size: 280,
strokeWidth: 20,
gapSize: 8, // Smaller gaps
}}
/>Custom Animation Timing
The spring configuration can be modified in the component:
typescript
// Faster animations
const SPRING_CONFIG = {
damping: 10,
stiffness: 150,
};
// Slower animations
const SPRING_CONFIG = {
damping: 25,
stiffness: 80,
};