Introduction
Components
- Accordion
- Action Sheet
- Alert
- Audio Player
- Audio Recorder
- Audio Waveform
- Avatar
- Badge
- BottomSheet
- Button
- Camera
- Camera Preview
- Card
- Carousel
- Checkbox
- Collapsible
- Color Picker
- Combobox
- Date Picker
- File Picker
- Gallery
- Hello Wave
- Icon
- Image
- Input
- Input OTP
- Link
- MediaPicker
- Mode Toggle
- Onboarding
- ParallaxScrollView
- Picker
- Popover
- Progress
- Radio
- ScrollView
- SearchBar
- Separator
- Share
- Sheet
- Skeleton
- Spinner
- Switch
- Table
- Tabs
- Text
- Toast
- Toggle
- Video
- View
Charts
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useEffect, useState } from 'react';
export function AudioWaveformDemo() {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
// Simulate audio playback progress
useEffect(() => {
let interval: number;
if (isPlaying) {
interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
setIsPlaying(false);
return 0;
}
return prev + 2;
});
}, 100);
}
return () => clearInterval(interval);
}, [isPlaying]);
const handleSeek = (position: number) => {
setProgress(position);
};
const togglePlayback = () => {
setIsPlaying(!isPlaying);
};
return (
<View style={{ gap: 16 }}>
<AudioWaveform
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={handleSeek}
height={60}
barCount={40}
/>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
<Button onPress={togglePlayback} style={{ flex: 0 }}>
{isPlaying ? 'Pause' : 'Play'}
</Button>
<Text variant='caption'>{Math.round(progress)}% Complete</Text>
</View>
</View>
);
}
Installation
pnpm dlx bna-ui add audio-waveform
Usage
import { AudioWaveform } from '@/components/ui/audio-waveform';
Basic Usage
function MyComponent() {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
return (
<AudioWaveform
isPlaying={isPlaying}
progress={progress}
showProgress={true}
height={60}
/>
);
}
With Custom Data
function MyComponent() {
const audioData = [0.2, 0.5, 0.8, 0.3, 0.6, 0.9, 0.4, 0.7, 0.1, 0.5];
return (
<AudioWaveform
data={audioData}
isPlaying={isPlaying}
progress={progress}
showProgress={true}
onSeek={(position) => setProgress(position)}
interactive={true}
/>
);
}
Interactive Seeking
function AudioPlayer() {
const [progress, setProgress] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [isSeeking, setIsSeeking] = useState(false);
const handleSeek = (position: number) => {
setProgress(position);
// Update your audio player position here
};
return (
<AudioWaveform
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={handleSeek}
onSeekStart={() => setIsSeeking(true)}
onSeekEnd={() => setIsSeeking(false)}
activeColor='#007AFF'
inactiveColor='#E5E5E7'
/>
);
}
Examples
Default
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useEffect, useState } from 'react';
export function AudioWaveformDemo() {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
// Simulate audio playback progress
useEffect(() => {
let interval: number;
if (isPlaying) {
interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
setIsPlaying(false);
return 0;
}
return prev + 2;
});
}, 100);
}
return () => clearInterval(interval);
}, [isPlaying]);
const handleSeek = (position: number) => {
setProgress(position);
};
const togglePlayback = () => {
setIsPlaying(!isPlaying);
};
return (
<View style={{ gap: 16 }}>
<AudioWaveform
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={handleSeek}
height={60}
barCount={40}
/>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
<Button onPress={togglePlayback} style={{ flex: 0 }}>
{isPlaying ? 'Pause' : 'Play'}
</Button>
<Text variant='caption'>{Math.round(progress)}% Complete</Text>
</View>
</View>
);
}
Recording Mode
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useEffect, useState } from 'react';
export function AudioWaveformRecording() {
const [isRecording, setIsRecording] = useState(false);
const [recordingData, setRecordingData] = useState<number[]>([]);
const [duration, setDuration] = useState(0);
// Simulate recording with real-time audio levels
useEffect(() => {
let interval: number;
if (isRecording) {
interval = setInterval(() => {
// Generate random audio level (simulating microphone input)
const newLevel = Math.max(
0.1,
Math.random() * 0.9 + Math.sin(Date.now() / 200) * 0.2
);
setRecordingData((prev) => {
const newData = [...prev, newLevel];
// Keep only the last 50 data points
return newData.slice(-50);
});
setDuration((prev) => prev + 0.1);
}, 100);
}
return () => clearInterval(interval);
}, [isRecording]);
const toggleRecording = () => {
if (!isRecording) {
setRecordingData([]);
setDuration(0);
}
setIsRecording(!isRecording);
};
const formatDuration = (seconds: number) => {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
};
return (
<View style={{ gap: 16 }}>
<View
style={{
backgroundColor: isRecording ? '#FF3B30' : '#34C759',
padding: 12,
borderRadius: 12,
opacity: 0.1,
}}
/>
<AudioWaveform
data={recordingData}
isPlaying={isRecording}
animated={true}
showProgress={false}
height={60}
barCount={50}
activeColor={isRecording ? '#FF3B30' : '#34C759'}
style={{
backgroundColor: isRecording
? 'rgba(255, 59, 48, 0.05)'
: 'rgba(52, 199, 89, 0.05)',
borderRadius: 8,
paddingHorizontal: 8,
}}
/>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
}}
>
<Button
onPress={toggleRecording}
variant={isRecording ? 'destructive' : 'default'}
>
{isRecording ? 'Stop Recording' : 'Start Recording'}
</Button>
<Text variant='caption'>{formatDuration(duration)}</Text>
</View>
{recordingData.length > 0 && !isRecording && (
<Text variant='caption' style={{ textAlign: 'center', opacity: 0.6 }}>
Tap play to preview your recording
</Text>
)}
</View>
);
}
Interactive Seeking
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useEffect, useState } from 'react';
export function AudioWaveformInteractive() {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const [isSeeking, setIsSeeking] = useState(false);
// Sample audio data - more realistic pattern
const audioData = [
0.2, 0.4, 0.3, 0.6, 0.8, 0.5, 0.7, 0.9, 0.4, 0.3, 0.5, 0.7, 0.6, 0.8, 0.9,
0.7, 0.5, 0.3, 0.4, 0.6, 0.8, 0.9, 0.7, 0.5, 0.4, 0.6, 0.8, 0.7, 0.5, 0.3,
0.4, 0.6, 0.9, 0.8, 0.6, 0.4, 0.2, 0.3, 0.5, 0.7,
];
// Auto-play simulation
useEffect(() => {
let interval: number;
if (isPlaying && !isSeeking) {
interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
setIsPlaying(false);
return 100;
}
return prev + 1;
});
}, 100);
}
return () => clearInterval(interval);
}, [isPlaying, isSeeking]);
const handleSeek = (position: number) => {
setProgress(position);
};
const handleSeekStart = () => {
setIsSeeking(true);
};
const handleSeekEnd = () => {
setIsSeeking(false);
};
const togglePlayback = () => {
setIsPlaying(!isPlaying);
};
const formatTime = (percentage: number) => {
const totalSeconds = 180; // 3 minutes total
const currentSeconds = (percentage / 100) * totalSeconds;
const minutes = Math.floor(currentSeconds / 60);
const seconds = Math.floor(currentSeconds % 60);
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
return (
<View style={{ gap: 16 }}>
<AudioWaveform
data={audioData}
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={handleSeek}
onSeekStart={handleSeekStart}
onSeekEnd={handleSeekEnd}
height={80}
barCount={40}
barWidth={4}
barGap={2}
activeColor='#007AFF'
inactiveColor='#E5E5E7'
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 12,
}}
>
<Text variant='caption' style={{ opacity: 0.6 }}>
{formatTime(progress)}
</Text>
<Text variant='caption' style={{ opacity: 0.6 }}>
3:00
</Text>
</View>
<View style={{ flexDirection: 'row', gap: 12, alignItems: 'center' }}>
<Button onPress={togglePlayback}>{isPlaying ? 'Pause' : 'Play'}</Button>
<Button
variant='outline'
onPress={() => setProgress(0)}
disabled={progress === 0}
>
Reset
</Button>
{isSeeking && (
<Text variant='caption' style={{ color: '#007AFF' }}>
Seeking...
</Text>
)}
</View>
</View>
);
}
Custom Styling
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';
export function AudioWaveformStyled() {
const [isPlaying1, setIsPlaying1] = useState(false);
const [isPlaying2, setIsPlaying2] = useState(false);
const [isPlaying3, setIsPlaying3] = useState(false);
const [progress1, setProgress1] = useState(35);
const [progress2, setProgress2] = useState(60);
const [progress3, setProgress3] = useState(80);
const musicData = [
0.1, 0.3, 0.5, 0.4, 0.6, 0.8, 0.7, 0.9, 0.6, 0.4, 0.5, 0.7, 0.8, 0.9, 0.7,
0.5, 0.3, 0.4, 0.6, 0.8, 0.9, 0.7, 0.5, 0.4, 0.6, 0.8, 0.7, 0.5, 0.3, 0.4,
];
const voiceData = [
0.2, 0.4, 0.3, 0.5, 0.4, 0.6, 0.5, 0.7, 0.4, 0.3, 0.5, 0.4, 0.6, 0.5, 0.4,
0.3, 0.4, 0.5, 0.6, 0.4, 0.3, 0.5, 0.4, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.3,
];
const podcastData = [
0.3, 0.5, 0.4, 0.6, 0.5, 0.4, 0.6, 0.7, 0.5, 0.4, 0.6, 0.5, 0.7, 0.6, 0.5,
0.4, 0.5, 0.6, 0.7, 0.5, 0.4, 0.6, 0.5, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.4,
];
return (
<View style={{ gap: 24 }}>
{/* Music Style - Vibrant gradient colors */}
<View
style={{
padding: 16,
backgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
borderRadius: 16,
shadowColor: '#667eea',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
}}
>
<Text
variant='body'
style={{ color: 'white', marginBottom: 12, fontWeight: 'bold' }}
>
🎵 Music Track
</Text>
<AudioWaveform
data={musicData}
isPlaying={isPlaying1}
progress={progress1}
showProgress={true}
interactive={true}
onSeek={setProgress1}
height={60}
barCount={30}
barWidth={4}
barGap={3}
activeColor='#FFD700'
inactiveColor='rgba(255, 255, 255, 0.3)'
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 12,
}}
>
<Button
onPress={() => setIsPlaying1(!isPlaying1)}
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
>
{isPlaying1 ? '⏸️' : '▶️'}
</Button>
<Text style={{ color: 'white', opacity: 0.8 }}>2:15 / 3:45</Text>
</View>
</View>
{/* Voice Message Style - Clean and minimal */}
<View
style={{
padding: 16,
backgroundColor: 'teal',
borderRadius: 12,
borderLeftWidth: 4,
borderLeftColor: '#34C759',
}}
>
<Text variant='body' style={{ marginBottom: 12, fontWeight: '600' }}>
🎙️ Voice Message
</Text>
<AudioWaveform
data={voiceData}
isPlaying={isPlaying2}
progress={progress2}
showProgress={true}
interactive={true}
onSeek={setProgress2}
height={40}
barCount={25}
barWidth={3}
barGap={2}
activeColor='#34C759'
inactiveColor='#E5E5E7'
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 8,
}}
>
<Button
onPress={() => setIsPlaying2(!isPlaying2)}
variant='outline'
style={{
borderColor: '#34C759',
borderRadius: 20,
paddingHorizontal: 16,
}}
>
{isPlaying2 ? 'Pause' : 'Play'}
</Button>
<Text variant='caption'>0:45</Text>
</View>
</View>
{/* Podcast Style - Professional dark theme */}
<View
style={{
padding: 16,
backgroundColor: '#1C1C1E',
borderRadius: 12,
borderWidth: 1,
borderColor: '#2C2C2E',
}}
>
<Text
variant='body'
style={{ color: 'white', marginBottom: 12, fontWeight: 'bold' }}
>
🎧 Podcast Episode
</Text>
<AudioWaveform
data={podcastData}
isPlaying={isPlaying3}
progress={progress3}
showProgress={true}
interactive={true}
onSeek={setProgress3}
height={70}
barCount={35}
barWidth={2}
barGap={1}
activeColor='#FF6B6B'
inactiveColor='#48484A'
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 12,
}}
>
<View style={{ flexDirection: 'row', gap: 8 }}>
<Button
onPress={() => setIsPlaying3(!isPlaying3)}
style={{
backgroundColor: '#FF6B6B',
borderRadius: 8,
paddingHorizontal: 12,
}}
>
{isPlaying3 ? '⏸️' : '▶️'}
</Button>
<Button
variant='outline'
style={{
borderColor: '#48484A',
borderRadius: 8,
}}
>
1.5x
</Button>
</View>
<Text style={{ color: 'white', opacity: 0.6 }}>45:30 / 58:15</Text>
</View>
</View>
</View>
);
}
Real-time Data
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useEffect, useState } from 'react';
export function AudioWaveformRealtime() {
const [isActive, setIsActive] = useState(false);
const [realtimeData, setRealtimeData] = useState<number[]>([]);
const [frequency, setFrequency] = useState(1);
const [amplitude, setAmplitude] = useState(0.5);
// Simulate real-time audio data with different patterns
useEffect(() => {
let interval: number;
if (isActive) {
interval = setInterval(() => {
const time = Date.now() / 1000;
// Generate different wave patterns based on frequency and amplitude
let newLevel = 0;
if (frequency === 1) {
// Sine wave
newLevel = Math.abs(Math.sin(time * 2) * amplitude);
} else if (frequency === 2) {
// Multiple frequencies combined
newLevel = Math.abs(
((Math.sin(time * 3) +
Math.sin(time * 1.5) +
Math.sin(time * 0.8)) /
3) *
amplitude
);
} else {
// Random with trend
newLevel = Math.max(
0,
Math.min(1, Math.random() * amplitude + Math.sin(time * 0.5) * 0.3)
);
}
setRealtimeData((prev) => {
const newData = [...prev, newLevel];
// Keep only the last 60 data points for smooth animation
return newData.slice(-60);
});
}, 50);
}
return () => clearInterval(interval);
}, [isActive, frequency, amplitude]);
const resetData = () => {
setRealtimeData([]);
};
const patternButtons = [
{ id: 1, label: 'Sine Wave', color: '#007AFF' },
{ id: 2, label: 'Complex', color: '#34C759' },
{ id: 3, label: 'Random', color: '#FF9500' },
];
return (
<View style={{ gap: 16 }}>
<View
style={{
padding: 16,
backgroundColor: isActive
? 'rgba(0, 122, 255, 0.05)'
: 'rgba(142, 142, 147, 0.05)',
borderRadius: 12,
borderWidth: 2,
borderColor: isActive ? '#007AFF' : '#E5E5E7',
borderStyle: isActive ? 'solid' : 'dashed',
}}
>
<Text variant='body' style={{ marginBottom: 12, fontWeight: 'bold' }}>
Real-time Audio Visualization
</Text>
<AudioWaveform
data={realtimeData}
isPlaying={isActive}
animated={true}
showProgress={false}
height={80}
barCount={60}
barWidth={2}
barGap={1}
activeColor={
frequency === 1
? '#007AFF'
: frequency === 2
? '#34C759'
: '#FF9500'
}
inactiveColor='#E5E5E7'
/>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 12,
}}
>
<Text variant='caption' style={{ opacity: 0.6 }}>
{realtimeData.length} data points
</Text>
<Text variant='caption' style={{ opacity: 0.6 }}>
{isActive ? 'Live' : 'Stopped'}
</Text>
</View>
</View>
{/* Controls */}
<View style={{ gap: 12 }}>
<View style={{ flexDirection: 'row', gap: 8 }}>
<Button
onPress={() => setIsActive(!isActive)}
style={{
backgroundColor: isActive ? '#FF3B30' : '#34C759',
flex: 1,
}}
>
{isActive ? 'Stop Stream' : 'Start Stream'}
</Button>
<Button
onPress={resetData}
variant='outline'
disabled={realtimeData.length === 0}
>
Clear
</Button>
</View>
{/* Pattern Selection */}
<View>
<Text variant='caption' style={{ marginBottom: 8, opacity: 0.7 }}>
Wave Pattern:
</Text>
<View style={{ flexDirection: 'row', gap: 8 }}>
{patternButtons.map((pattern) => (
<Button
key={pattern.id}
onPress={() => setFrequency(pattern.id)}
variant={frequency === pattern.id ? 'default' : 'outline'}
style={{
flex: 1,
backgroundColor:
frequency === pattern.id ? pattern.color : 'transparent',
borderColor: pattern.color,
}}
>
<Text
style={{
color: frequency === pattern.id ? 'white' : pattern.color,
fontSize: 12,
}}
>
{pattern.label}
</Text>
</Button>
))}
</View>
</View>
{/* Amplitude Control */}
<View>
<Text variant='caption' style={{ marginBottom: 8, opacity: 0.7 }}>
Amplitude: {Math.round(amplitude * 100)}%
</Text>
<View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
<Button
size='icon'
variant='outline'
onPress={() => setAmplitude(Math.max(0.1, amplitude - 0.1))}
>
-
</Button>
<View
style={{
flex: 1,
height: 4,
backgroundColor: '#E5E5E7',
borderRadius: 2,
overflow: 'hidden',
}}
>
<View
style={{
width: `${amplitude * 100}%`,
height: '100%',
backgroundColor: '#007AFF',
}}
/>
</View>
<Button
size='icon'
variant='outline'
onPress={() => setAmplitude(Math.min(1, amplitude + 0.1))}
>
+
</Button>
</View>
</View>
</View>
<View
style={{
padding: 12,
backgroundColor: 'rgba(255, 149, 0, 0.05)',
borderRadius: 8,
}}
>
<Text variant='caption' style={{ textAlign: 'center', opacity: 0.7 }}>
Simulates real-time audio input with different wave patterns
</Text>
</View>
</View>
);
}
Compact Size
import { AudioWaveform } from '@/components/ui/audio-waveform';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';
export function AudioWaveformCompact() {
const [isPlaying1, setIsPlaying1] = useState(false);
const [isPlaying2, setIsPlaying2] = useState(false);
const [isPlaying3, setIsPlaying3] = useState(false);
const [progress1, setProgress1] = useState(20);
const [progress2, setProgress2] = useState(45);
const [progress3, setProgress3] = useState(70);
// Compact chat message data
const messageData1 = [
0.3, 0.5, 0.4, 0.6, 0.3, 0.4, 0.5, 0.3, 0.4, 0.6, 0.5, 0.3, 0.4, 0.5, 0.3,
];
const messageData2 = [
0.2, 0.4, 0.6, 0.5, 0.3, 0.5, 0.4, 0.6, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.3,
];
const messageData3 = [
0.4, 0.6, 0.5, 0.7, 0.4, 0.3, 0.5, 0.6, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.4,
];
const MessageBubble = ({
data,
isPlaying,
setIsPlaying,
progress,
setProgress,
duration,
sent = false,
}: {
data: number[];
isPlaying: boolean;
setIsPlaying: (playing: boolean) => void;
progress: number;
setProgress: (progress: number) => void;
duration: string;
sent?: boolean;
}) => (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
alignSelf: sent ? 'flex-end' : 'flex-start',
backgroundColor: sent ? '#007AFF' : '#F0F0F0',
borderRadius: 18,
padding: 8,
maxWidth: '80%',
gap: 8,
}}
>
<Button
onPress={() => setIsPlaying(!isPlaying)}
variant='ghost'
size='icon'
>
{isPlaying ? '⏸️' : '▶️'}
</Button>
<View style={{ flex: 1 }}>
<AudioWaveform
data={data}
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={setProgress}
height={24}
barCount={15}
barWidth={2}
barGap={1}
activeColor={sent ? 'white' : '#007AFF'}
inactiveColor={sent ? 'rgba(255, 255, 255, 0.4)' : '#C7C7CC'}
/>
</View>
<Text
style={{
color: sent ? 'rgba(255, 255, 255, 0.8)' : '#8E8E93',
fontSize: 11,
minWidth: 28,
}}
>
{duration}
</Text>
</View>
);
return (
<View style={{ gap: 16 }}>
<View style={{ gap: 12 }}>
<MessageBubble
data={messageData1}
isPlaying={isPlaying1}
setIsPlaying={setIsPlaying1}
progress={progress1}
setProgress={setProgress1}
duration='0:12'
sent={false}
/>
<MessageBubble
data={messageData2}
isPlaying={isPlaying2}
setIsPlaying={setIsPlaying2}
progress={progress2}
setProgress={setProgress2}
duration='0:08'
sent={true}
/>
<MessageBubble
data={messageData3}
isPlaying={isPlaying3}
setIsPlaying={setIsPlaying3}
progress={progress3}
setProgress={setProgress3}
duration='0:15'
sent={false}
/>
</View>
</View>
);
}
API Reference
AudioWaveform
The main AudioWaveform component.
Prop | Type | Default | Description |
---|---|---|---|
data | number[] | - | Audio amplitude data (0-1 range). Auto-generated if not provided. |
isPlaying | boolean | false | Whether the audio is currently playing. |
progress | number | 0 | Current playback progress (0-100). |
onSeek | (position: number) => void | - | Callback fired when user seeks to a position. |
onSeekStart | () => void | - | Callback fired when seeking starts. |
onSeekEnd | () => void | - | Callback fired when seeking ends. |
style | ViewStyle | - | Additional styles for the container. |
height | number | 60 | Height of the waveform in pixels. |
barCount | number | 50 | Number of bars in the waveform. |
barWidth | number | 3 | Width of each bar in pixels. |
barGap | number | 2 | Gap between bars in pixels. |
activeColor | string | theme color | Color for played/active portion. |
inactiveColor | string | theme color | Color for unplayed/inactive portion. |
animated | boolean | true | Whether to animate the waveform during playback. |
showProgress | boolean | false | Whether to show progress indicator. |
interactive | boolean | false | Whether to enable touch-based seeking. |
Usage Patterns
Audio Player Integration
function AudioPlayer({ audioUrl }: { audioUrl: string }) {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
const [duration, setDuration] = useState(0);
const [audioData, setAudioData] = useState<number[]>([]);
// Load audio data from file
useEffect(() => {
loadAudioWaveform(audioUrl).then(setAudioData);
}, [audioUrl]);
const handleSeek = (position: number) => {
const newTime = (position / 100) * duration;
// Seek audio to newTime
setProgress(position);
};
return (
<View>
<AudioWaveform
data={audioData}
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
onSeek={handleSeek}
height={80}
activeColor='#007AFF'
/>
<Button onPress={() => setIsPlaying(!isPlaying)}>
{isPlaying ? 'Pause' : 'Play'}
</Button>
</View>
);
}
Voice Message
function VoiceMessage({ duration, audioData }: VoiceMessageProps) {
const [isPlaying, setIsPlaying] = useState(false);
const [progress, setProgress] = useState(0);
return (
<View style={styles.voiceMessage}>
<TouchableOpacity onPress={() => setIsPlaying(!isPlaying)}>
<Icon name={isPlaying ? 'pause' : 'play'} />
</TouchableOpacity>
<AudioWaveform
data={audioData}
isPlaying={isPlaying}
progress={progress}
showProgress={true}
interactive={true}
height={32}
barCount={30}
barWidth={2}
barGap={1}
style={{ flex: 1, marginHorizontal: 12 }}
/>
<Text>{formatDuration(duration)}</Text>
</View>
);
}
Recording Visualization
function RecordingView() {
const [isRecording, setIsRecording] = useState(false);
const [recordingData, setRecordingData] = useState<number[]>([]);
// Update waveform with real-time audio levels
useEffect(() => {
if (isRecording) {
const interval = setInterval(() => {
const newLevel = getCurrentAudioLevel(); // Your audio level function
setRecordingData((prev) => [...prev.slice(-49), newLevel]);
}, 100);
return () => clearInterval(interval);
}
}, [isRecording]);
return (
<View>
<AudioWaveform
data={recordingData}
isPlaying={isRecording}
animated={true}
showProgress={false}
height={60}
barCount={50}
activeColor='#FF3B30'
/>
<Button onPress={() => setIsRecording(!isRecording)}>
{isRecording ? 'Stop' : 'Record'}
</Button>
</View>
);
}
Accessibility
The AudioWaveform component follows accessibility best practices:
- Proper touch target sizes for interactive elements
- Screen reader support for progress information
- Respects system accessibility settings
- Keyboard navigation support where applicable
Performance
The component is optimized for performance:
- Uses
react-native-reanimated
for smooth native animations - Efficient rendering with minimal re-renders
- Configurable bar count to balance detail vs performance
- Native driver animations where possible
Theming
The AudioWaveform automatically adapts to your app's theme:
- Uses theme colors by default for active/inactive states
- Supports both light and dark modes
- Customizable colors through props
- Respects theme opacity values
Data Format
Audio data should be provided as an array of numbers between 0 and 1:
const audioData = [
0.1, // Very quiet
0.3, // Quiet
0.5, // Medium
0.8, // Loud
1.0, // Maximum
];
If no data is provided, the component generates realistic sample data automatically.
On This Page
InstallationUsageBasic UsageWith Custom DataInteractive SeekingExamplesDefaultRecording ModeInteractive SeekingCustom StylingReal-time DataCompact SizeAPI ReferenceAudioWaveformUsage PatternsAudio Player IntegrationVoice MessageRecording VisualizationAccessibilityPerformanceThemingData Format