Skip to content

Progress

Progress provides a comprehensive solution for displaying progress indicators with smooth animations, multiple variants, and flexible styling options. It supports both static and animated progress tracking with customizable colors and sizes.

import { Progress, ProgressFilledTrack } from 'rnc-theme';
<Progress value={65}>
<ProgressFilledTrack />
</Progress>
PropTypeDefaultDescription
valuenumber0Current progress value (0-100)
maxnumber100Maximum progress value
sizeComponentSize'md'Progress bar size (xs, sm, md, lg, xl)
variantComponentVariant'default'Visual style variant
trackColorkeyof Theme['colors']theme.colors.borderBackground track color
animatedbooleantrueEnable smooth progress animations
styleStyleProp<ViewStyle>-Additional container styles
childrenReact.ReactNode-ProgressFilledTrack components
PropTypeDefaultDescription
colorkeyof Theme['colors']-Custom fill color (overrides variant)
styleStyleProp<ViewStyle>-Additional track styles
VariantDescriptionUse Case
defaultDefault progress styleGeneral progress indicators
primaryPrimary theme colorMain progress tracking
secondarySecondary theme colorAlternative progress
successSuccess/completion colorSuccess states, completion
errorError/danger colorError states, failed operations
warningWarning colorWarning states, caution
infoInformation colorInfo states, neutral progress
destructiveDestructive action colorDestructive operations
SizeHeightUse Case
xs2pxMinimal progress indicators
sm4pxCompact UI elements
md8pxStandard progress bars
lg12pxProminent progress display
xl16pxLarge, highly visible progress
const FileUploader = () => {
const [uploadProgress, setUploadProgress] = useState(0);
const [isUploading, setIsUploading] = useState(false);
const handleUpload = async (file) => {
setIsUploading(true);
setUploadProgress(0);
// Simulate upload with progress updates
const interval = setInterval(() => {
setUploadProgress(prev => {
if (prev >= 100) {
clearInterval(interval);
setIsUploading(false);
return 100;
}
return prev + Math.random() * 15;
});
}, 200);
};
return (
<VStack spacing="md" padding="lg">
<Text>Upload Progress: {Math.round(uploadProgress)}%</Text>
<Progress
value={uploadProgress}
variant="primary"
size="md"
>
<ProgressFilledTrack />
</Progress>
<HStack spacing="md" justify="space-between">
<Button
onPress={handleUpload}
disabled={isUploading}
variant="primary"
>
<ButtonText>
{isUploading ? 'Uploading...' : 'Start Upload'}
</ButtonText>
</Button>
{isUploading && (
<Text style={{ fontSize: 12, color: theme.colors.muted }}>
{uploadProgress < 100 ? 'Uploading file...' : 'Upload complete!'}
</Text>
)}
</HStack>
</VStack>
);
};
const MultiStepForm = () => {
const [currentStep, setCurrentStep] = useState(1);
const totalSteps = 4;
const progress = (currentStep / totalSteps) * 100;
const steps = [
'Personal Info',
'Contact Details',
'Preferences',
'Review & Submit'
];
return (
<VStack spacing="lg" padding="xl">
{/* Progress Header */}
<VStack spacing="sm">
<HStack justify="space-between">
<Text style={{ fontWeight: 'bold' }}>
Step {currentStep} of {totalSteps}
</Text>
<Text style={{ color: theme.colors.muted }}>
{Math.round(progress)}% Complete
</Text>
</HStack>
<Progress value={progress} variant="primary" size="sm">
<ProgressFilledTrack />
</Progress>
<Text style={{ color: theme.colors.muted }}>
{steps[currentStep - 1]}
</Text>
</VStack>
{/* Form Content */}
<Card padding="lg">
{/* Step content would go here */}
<Text>Form content for {steps[currentStep - 1]}</Text>
</Card>
{/* Navigation */}
<HStack spacing="md" justify="space-between">
<Button
variant="outline"
disabled={currentStep === 1}
onPress={() => setCurrentStep(prev => Math.max(1, prev - 1))}
>
<ButtonText>Previous</ButtonText>
</Button>
<Button
variant="primary"
onPress={() => setCurrentStep(prev => Math.min(totalSteps, prev + 1))}
>
<ButtonText>
{currentStep === totalSteps ? 'Submit' : 'Next'}
</ButtonText>
</Button>
</HStack>
</VStack>
);
};
const DataProcessor = () => {
const [processes, setProcesses] = useState([
{ id: 1, name: 'Data Validation', progress: 100, status: 'complete' },
{ id: 2, name: 'Data Processing', progress: 65, status: 'running' },
{ id: 3, name: 'Report Generation', progress: 0, status: 'pending' },
{ id: 4, name: 'Email Notification', progress: 0, status: 'pending' }
]);
const getVariantForStatus = (status) => {
switch (status) {
case 'complete': return 'success';
case 'running': return 'primary';
case 'error': return 'error';
case 'pending': return 'default';
default: return 'default';
}
};
const getStatusIcon = (status) => {
switch (status) {
case 'complete': return <CheckIcon size={16} />;
case 'running': return <ClockIcon size={16} />;
case 'error': return <XIcon size={16} />;
case 'pending': return <CircleIcon size={16} />;
default: return null;
}
};
return (
<VStack spacing="lg" padding="lg">
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>
Processing Pipeline
</Text>
{processes.map((process) => (
<Card key={process.id} padding="md">
<VStack spacing="sm">
<HStack justify="space-between" align="center">
<HStack spacing="sm" align="center">
{getStatusIcon(process.status)}
<Text style={{ fontWeight: '500' }}>
{process.name}
</Text>
</HStack>
<Text style={{
fontSize: 12,
color: theme.colors.muted
}}>
{process.progress}%
</Text>
</HStack>
<Progress
value={process.progress}
variant={getVariantForStatus(process.status)}
size="sm"
>
<ProgressFilledTrack />
</Progress>
</VStack>
</Card>
))}
<HStack spacing="md" justify="center">
<Button variant="outline" size="sm">
<ButtonText>Pause All</ButtonText>
</Button>
<Button variant="primary" size="sm">
<ButtonText>Resume</ButtonText>
</Button>
</HStack>
</VStack>
);
};
const ProfileCompletion = () => {
const profileSections = [
{ name: 'Basic Info', completed: 100, color: 'success' },
{ name: 'Profile Photo', completed: 100, color: 'success' },
{ name: 'Work Experience', completed: 75, color: 'primary' },
{ name: 'Skills & Endorsements', completed: 40, color: 'warning' },
{ name: 'Education', completed: 0, color: 'default' }
];
const overallCompletion = profileSections.reduce(
(acc, section) => acc + section.completed, 0
) / profileSections.length;
return (
<VStack spacing="lg" padding="lg">
{/* Overall Progress */}
<Card padding="lg">
<VStack spacing="md">
<HStack justify="space-between" align="center">
<Text style={{ fontSize: 18, fontWeight: 'bold' }}>
Profile Completion
</Text>
<Text style={{
fontSize: 16,
fontWeight: '600',
color: theme.colors.primary
}}>
{Math.round(overallCompletion)}%
</Text>
</HStack>
<Progress
value={overallCompletion}
variant="primary"
size="lg"
>
<ProgressFilledTrack />
</Progress>
<Text style={{
fontSize: 12,
color: theme.colors.muted,
textAlign: 'center'
}}>
Complete your profile to increase visibility
</Text>
</VStack>
</Card>
{/* Individual Sections */}
<VStack spacing="md">
<Text style={{ fontSize: 16, fontWeight: '600' }}>
Section Details
</Text>
{profileSections.map((section, index) => (
<HStack key={index} spacing="md" align="center">
<VStack spacing="xs" style={{ flex: 1 }}>
<HStack justify="space-between">
<Text style={{ fontSize: 14 }}>
{section.name}
</Text>
<Text style={{
fontSize: 12,
color: theme.colors.muted
}}>
{section.completed}%
</Text>
</HStack>
<Progress
value={section.completed}
variant={section.color}
size="xs"
>
<ProgressFilledTrack />
</Progress>
</VStack>
{section.completed < 100 && (
<Button variant="ghost" size="sm">
<ButtonIcon icon={<PlusIcon />} position="center" />
</Button>
)}
</HStack>
))}
</VStack>
</VStack>
);
};
const CustomProgress = () => {
const [progress, setProgress] = useState(45);
return (
<VStack spacing="lg" padding="lg">
{/* Gradient Progress */}
<VStack spacing="sm">
<Text>Gradient Progress</Text>
<Progress
value={progress}
size="md"
trackColor="background"
style={{
backgroundColor: theme.colors.muted,
borderRadius: 8
}}
>
<ProgressFilledTrack
style={{
background: 'linear-gradient(90deg, #FF6B6B, #4ECDC4)',
borderRadius: 8
}}
/>
</Progress>
</VStack>
{/* Striped Progress */}
<VStack spacing="sm">
<Text>Striped Progress</Text>
<Progress value={progress} variant="primary" size="lg">
<ProgressFilledTrack
style={{
backgroundImage: `repeating-linear-gradient(
45deg,
${theme.colors.primary},
${theme.colors.primary} 10px,
rgba(255,255,255,0.2) 10px,
rgba(255,255,255,0.2) 20px
)`,
backgroundSize: '20px 20px'
}}
/>
</Progress>
</VStack>
{/* Multi-Color Segments */}
<VStack spacing="sm">
<Text>Multi-Segment Progress</Text>
<View style={{
flexDirection: 'row',
height: 8,
borderRadius: 4,
backgroundColor: theme.colors.border,
overflow: 'hidden'
}}>
<View style={{
width: '30%',
backgroundColor: theme.colors.success,
}} />
<View style={{
width: '25%',
backgroundColor: theme.colors.warning,
}} />
<View style={{
width: '20%',
backgroundColor: theme.colors.error,
}} />
</View>
</VStack>
{/* Control Buttons */}
<HStack spacing="md" justify="center">
<Button
variant="outline"
size="sm"
onPress={() => setProgress(Math.max(0, progress - 10))}
>
<ButtonText>-10%</ButtonText>
</Button>
<Button
variant="outline"
size="sm"
onPress={() => setProgress(Math.min(100, progress + 10))}
>
<ButtonText>+10%</ButtonText>
</Button>
<Button
variant="ghost"
size="sm"
onPress={() => setProgress(0)}
>
<ButtonText>Reset</ButtonText>
</Button>
</HStack>
</VStack>
);
};
const LabeledProgress = ({ label, value, showPercentage = true }) => (
<VStack spacing="xs">
<HStack justify="space-between" align="center">
<Text style={{ fontSize: 14, fontWeight: '500' }}>
{label}
</Text>
{showPercentage && (
<Text style={{
fontSize: 12,
color: theme.colors.muted
}}>
{value}%
</Text>
)}
</HStack>
<Progress value={value} variant="primary" size="sm">
<ProgressFilledTrack />
</Progress>
</VStack>
);
// Usage
<VStack spacing="md">
<LabeledProgress label="JavaScript" value={85} />
<LabeledProgress label="React Native" value={78} />
<LabeledProgress label="TypeScript" value={65} />
</VStack>
const useAnimatedProgress = (targetValue, duration = 1000) => {
const [currentValue, setCurrentValue] = useState(0);
useEffect(() => {
const startTime = Date.now();
const startValue = currentValue;
const difference = targetValue - startValue;
const animateProgress = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
// Easing function (ease-out)
const easedProgress = 1 - Math.pow(1 - progress, 3);
const newValue = startValue + (difference * easedProgress);
setCurrentValue(newValue);
if (progress < 1) {
requestAnimationFrame(animateProgress);
}
};
requestAnimationFrame(animateProgress);
}, [targetValue, duration]);
return currentValue;
};
// Usage
const AnimatedProgressDemo = () => {
const [target, setTarget] = useState(0);
const animatedValue = useAnimatedProgress(target, 2000);
return (
<VStack spacing="md">
<Progress value={animatedValue} variant="primary" size="md">
<ProgressFilledTrack />
</Progress>
<Button onPress={() => setTarget(Math.random() * 100)}>
<ButtonText>Random Progress</ButtonText>
</Button>
</VStack>
);
};
<Progress value={75} animated={true}>
<ProgressFilledTrack />
</Progress>

Visual Feedback

  • Use appropriate variants to convey meaning (success for completion, error for failures)
  • Consider showing percentage values for precise progress tracking
  • Provide contextual labels to explain what progress represents

Performance

  • Use animated={false} for frequently updating progress bars
  • Throttle progress updates in high-frequency scenarios
  • Consider using React.memo for lists of progress components