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 { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerDemo() {
// Sample audio URL - replace with your actual audio source
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={true}
autoPlay={false}
onPlaybackStatusUpdate={(status) => {
console.log('Playback status:', status);
}}
/>
);
}
Installation
pnpm dlx bna-ui add audio-player
Usage
import { AudioPlayer } from '@/components/ui/audio-player';
Basic Usage
function MyComponent() {
return (
<AudioPlayer
source={{ uri: 'https://example.com/audio.mp3' }}
showControls={true}
showWaveform={true}
showTimer={true}
autoPlay={false}
/>
);
}
With Local File
<AudioPlayer
source={require('./assets/audio/sample.mp3')}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={true}
onPlaybackStatusUpdate={(status) => {
console.log('Playback status:', status);
}}
/>
Minimal Player
<AudioPlayer
source={{ uri: 'https://example.com/audio.mp3' }}
showControls={true}
showWaveform={false}
showTimer={false}
showProgressBar={true}
/>
Examples
Default
import { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerDemo() {
// Sample audio URL - replace with your actual audio source
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={true}
autoPlay={false}
onPlaybackStatusUpdate={(status) => {
console.log('Playback status:', status);
}}
/>
);
}
Minimal
import { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerMinimal() {
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={false}
showTimer={false}
showProgressBar={true}
autoPlay={false}
/>
);
}
Waveform Only
import { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerWaveform() {
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={false}
autoPlay={false}
/>
);
}
Custom Styling
import { AudioPlayer } from '@/components/ui/audio-player';
import { useThemeColor } from '@/hooks/useThemeColor';
export function AudioPlayerStyled() {
const blue = useThemeColor({}, 'indigo');
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={true}
autoPlay={false}
style={{
borderRadius: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 5,
backgroundColor: blue,
}}
/>
);
}
Auto Play
import { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerAutoplay() {
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={true}
autoPlay={true}
onPlaybackStatusUpdate={(status) => {
if (status.isLoaded && status.playing) {
console.log('Auto-playing audio');
}
}}
/>
);
}
Progress Bar Only
import { AudioPlayer } from '@/components/ui/audio-player';
export function AudioPlayerProgress() {
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={false}
showTimer={true}
showProgressBar={true}
autoPlay={false}
/>
);
}
Audio player Music
import { AudioPlayer } from '@/components/ui/audio-player';
import { Button } from '@/components/ui/button';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import {
Heart,
MoreHorizontal,
Shuffle,
SkipBack,
SkipForward,
} from 'lucide-react-native';
import React, { useState } from 'react';
import { Image, StyleSheet } from 'react-native';
export function AudioPlayerMusic() {
const [isLiked, setIsLiked] = useState(false);
const sampleAudioUrl =
'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav';
return (
<View style={styles.musicPlayer}>
{/* Album Art */}
<View style={styles.albumArtContainer}>
<Image
source={{
uri: 'https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=200&h=200&fit=crop&crop=center',
}}
style={styles.albumArt}
/>
</View>
{/* Track Information */}
<View style={styles.trackInfo}>
<Text variant='body' style={styles.trackTitle}>
Midnight Waves
</Text>
<Text variant='caption' style={styles.artistName}>
Ocean Sounds Orchestra
</Text>
</View>
{/* Action Buttons */}
<View style={styles.actionButtons}>
<Button
variant='ghost'
size='icon'
onPress={() => setIsLiked(!isLiked)}
style={styles.actionButton}
>
<Heart
size={20}
color={isLiked ? '#ff6b6b' : '#666'}
fill={isLiked ? '#ff6b6b' : 'transparent'}
/>
</Button>
<Button variant='ghost' size='icon' style={styles.actionButton}>
<MoreHorizontal size={20} color='#666' />
</Button>
</View>
{/* Audio Player */}
<AudioPlayer
source={{ uri: sampleAudioUrl }}
showControls={true}
showWaveform={true}
showTimer={true}
showProgressBar={false}
autoPlay={false}
style={styles.playerContainer}
/>
{/* Additional Controls */}
<View style={styles.additionalControls}>
<Button variant='ghost' size='icon' style={styles.controlButton}>
<Shuffle size={18} color='#666' />
</Button>
<Button variant='ghost' size='icon' style={styles.controlButton}>
<SkipBack size={18} color='#666' />
</Button>
<View style={styles.spacer} />
<Button variant='ghost' size='icon' style={styles.controlButton}>
<SkipForward size={18} color='#666' />
</Button>
<Button variant='ghost' size='icon' style={styles.controlButton}>
<Shuffle size={18} color='#666' />
</Button>
</View>
</View>
);
}
const styles = StyleSheet.create({
musicPlayer: {
backgroundColor: '#F2F2F7',
borderRadius: 16,
padding: 20,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 8,
},
albumArtContainer: {
alignItems: 'center',
marginBottom: 16,
},
albumArt: {
width: 120,
height: 120,
borderRadius: 12,
},
trackInfo: {
alignItems: 'center',
marginBottom: 12,
},
trackTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 4,
color: '#1a1a1a',
},
artistName: {
fontSize: 14,
color: '#666',
},
actionButtons: {
flexDirection: 'row',
justifyContent: 'center',
gap: 8,
marginBottom: 16,
},
actionButton: {
width: 36,
height: 36,
},
playerContainer: {
backgroundColor: 'transparent',
margin: 0,
padding: 0,
marginBottom: 16,
},
additionalControls: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
},
controlButton: {
width: 36,
height: 36,
},
spacer: {
width: 40,
},
});
API Reference
AudioPlayer
The main AudioPlayer component.
Prop | Type | Default | Description |
---|---|---|---|
source | AudioSource | - | The audio source to play (URI or local file). |
style | ViewStyle | - | Additional styles for the player container. |
showControls | boolean | true | Whether to show playback controls. |
showWaveform | boolean | true | Whether to show the waveform visualization. |
showTimer | boolean | true | Whether to show the current time and duration. |
showProgressBar | boolean | true | Whether to show the progress bar. |
autoPlay | boolean | false | Whether to start playing automatically when loaded. |
onPlaybackStatusUpdate | (status: any) => void | - | Callback fired when playback status changes. |
AudioSource
The audio source configuration.
type AudioSource =
| {
uri: string;
}
| number; // For local files using require()
Playback Status
The status object passed to onPlaybackStatusUpdate
:
type PlaybackStatus = {
isLoaded: boolean;
playing: boolean;
duration: number;
position: number;
};
Features
Waveform Visualization
The audio player includes an interactive waveform that:
- Shows audio amplitude visualization with 60 bars
- Displays playback progress with visual feedback
- Supports seeking by tapping/clicking on the waveform
- Animates smoothly during playback
- Uses theme colors for active/inactive states
Playback Controls
Standard playback controls include:
- Play/Pause: Toggle audio playback
- Back 5 seconds: Skip backward 5 seconds
- Restart: Return to the beginning of the track
Interactive Progress Bar
An alternative to waveform seeking:
- Shows current playback position
- Allows seeking by dragging or tapping
- Smooth visual feedback during interaction
- Respects theme colors
Timer Display
Shows current position and total duration:
- Format:
MM:SS / MM:SS
- Updates in real-time during playback
- Uses muted text color from theme
Platform Support
The AudioPlayer works across all platforms supported by Expo:
- iOS: Native audio playback with hardware control integration
- Android: Optimized audio engine with proper lifecycle management
- Web: HTML5 audio with fallback support
Accessibility
The AudioPlayer component follows accessibility best practices:
- Screen reader announcements for control actions
- Proper button labeling and roles
- Keyboard navigation support (web)
- Respects system accessibility settings
- High contrast support for visually impaired users
Performance
The component is optimized for performance:
- Efficient waveform rendering with limited update frequency
- Smooth animations using native drivers where possible
- Memory-efficient audio loading and cleanup
- Minimal re-renders during playback
Theming
The AudioPlayer automatically adapts to your app's theme:
- Uses theme colors for backgrounds, text, and accents
- Supports both light and dark modes
- Destructive color for the main play button
- Muted colors for inactive states
- Customizable through theme configuration
Advanced Usage
Custom Playback Status Handling
<AudioPlayer
source={{ uri: 'https://example.com/podcast.mp3' }}
onPlaybackStatusUpdate={(status) => {
if (status.isLoaded && status.playing) {
// Track listening analytics
analytics.track('audio_playing', {
position: status.position,
duration: status.duration,
});
}
}}
/>
Responsive Design
<AudioPlayer
source={{ uri: 'https://example.com/music.mp3' }}
style={{
maxWidth: 400,
alignSelf: 'center',
}}
showWaveform={Platform.OS !== 'web'} // Hide on web for better performance
/>
Troubleshooting
Audio Not Loading
- Ensure the audio source URL is accessible
- Check network connectivity
- Verify audio format is supported (MP3, AAC, WAV)
- Check for CORS issues on web platform
Performance Issues
- Reduce waveform bar count for lower-end devices
- Disable animations on older devices
- Use lower quality audio files for better loading times
Seeking Issues
- Ensure audio file supports seeking (not all streaming formats do)
- Check if the audio source provides duration metadata
- Verify the audio file is not corrupted
On This Page
InstallationUsageBasic UsageWith Local FileMinimal PlayerExamplesDefaultMinimalWaveform OnlyCustom StylingAuto PlayProgress Bar OnlyAudio player MusicAPI ReferenceAudioPlayerAudioSourcePlayback StatusFeaturesWaveform VisualizationPlayback ControlsInteractive Progress BarTimer DisplayPlatform SupportAccessibilityPerformanceThemingAdvanced UsageCustom Playback Status HandlingResponsive DesignTroubleshootingAudio Not LoadingPerformance IssuesSeeking Issues