logoChisa UI

Command Palette

Search for a command to run...

Componentschevron_rightProgressBar

ProgressBar

Animated progress bar with line and wavy variants. The wavy variant uses SVG Bezier curves for a smooth, organic wave effect.

9:41
signal_cellular_altwifibattery_full
Line Variant
Wavy Variant
Progress50%

Drag the slider to adjust progress. Both variants animate smoothly.

Installation

$

Usage

Example.tsx
import { ProgressBar } from './components/ProgressBar';
export default function App() {
const [progress, setProgress] = useState(50);
return (
<View style={{ padding: 16 }}>
{/* Line variant */}
<ProgressBar progress={progress} variant="line" />
{/* Wavy variant */}
<ProgressBar
progress={progress}
variant="wavy"
wavelength={25}
amplitude={0.6}
color="#cf350b"
/>
</View>
);
}

Full Component Code

ProgressBar.tsx
1import React, { useEffect } from 'react';
2import { LayoutChangeEvent, StyleSheet, View, ViewStyle } from 'react-native';
3import Animated, {
4 useAnimatedStyle,
5 useSharedValue,
6 withSpring,
7} from 'react-native-reanimated';
8import Svg, { Defs, Mask, Path, Rect } from 'react-native-svg';
9
10export type ProgressBarVariant = 'line' | 'wavy';
11
12interface ProgressBarProps {
13 progress: number;
14 variant?: ProgressBarVariant;
15 height?: number;
16 color?: string;
17 trackColor?: string;
18 strokeWidth?: number;
19 gapSize?: number;
20 amplitude?: number;
21 wavelength?: number;
22 style?: ViewStyle;
23}
24
25export const ProgressBar: React.FC<ProgressBarProps> = ({
26 progress,
27 variant = 'line',
28 height: propHeight,
29 color = '#cf350b',
30 trackColor = '#E5E7EB',
31 strokeWidth = 4,
32 gapSize = 8,
33 amplitude = 0.6,
34 wavelength = 50,
35 style,
36}) => {
37 const minWavyHeight = strokeWidth * 9;
38 const height = propHeight ?? (variant === 'wavy' ? minWavyHeight : 8);
39 const effectiveHeight = variant === 'wavy' ? Math.max(height, minWavyHeight) : height;
40
41 const [width, setWidth] = React.useState(0);
42 const progressValue = useSharedValue(0);
43
44 useEffect(() => {
45 progressValue.value = withSpring(Math.min(Math.max(progress, 0), 100), {
46 damping: 15,
47 stiffness: 100,
48 });
49 }, [progress]);
50
51 const onLayout = (event: LayoutChangeEvent) => {
52 setWidth(event.nativeEvent.layout.width);
53 };
54
55 if (variant === 'wavy') {
56 const canvasHeight = effectiveHeight;
57 return (
58 <View onLayout={onLayout} style={[{ width: '100%', height: canvasHeight }, style]}>
59 {width > 0 && (
60 <WavyBar
61 progress={progressValue}
62 width={width}
63 height={effectiveHeight}
64 canvasHeight={canvasHeight}
65 color={color}
66 trackColor={trackColor}
67 strokeWidth={strokeWidth}
68 gapSize={gapSize}
69 amplitude={amplitude}
70 wavelength={wavelength}
71 />
72 )}
73 </View>
74 );
75 }
76
77 const animatedStyle = useAnimatedStyle(() => ({
78 width: `${progressValue.value}%`,
79 }));
80
81 return (
82 <View onLayout={onLayout} style={[styles.container, styles.rounded, { height: effectiveHeight, backgroundColor: trackColor }, style]}>
83 <Animated.View style={[styles.fill, styles.rounded, { backgroundColor: color }, animatedStyle]} />
84 </View>
85 );
86};
87
88// WavyBar component uses SVG with Bezier curves
89const WavyBar = ({ /* props */ }) => {
90 // See full implementation for SVG wave rendering
91};
92
93const styles = StyleSheet.create({
94 container: { width: '100%', overflow: 'hidden' },
95 rounded: { borderRadius: 9999 },
96 fill: { height: '100%' },
97});
98
99export default ProgressBar;

Props

NameTypeDefaultDescription
progressnumber0Progress value from 0 to 100
variant'line' | 'wavy''line'Visual style of progress bar
heightnumber8 (line) / 36 (wavy)Height of the progress bar
colorstring'#cf350b'Fill color for progress
trackColorstring'#E5E7EB'Track background color
strokeWidthnumber4Stroke width for wavy variant
gapSizenumber8Gap between fill and track (wavy)
amplitudenumber0.6Wave amplitude (0-1)
wavelengthnumber50Wave length in pixels