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 { AudioRecorder } from '@/components/ui/audio-recorder';
export function AudioRecorderDemo() {
const handleRecordingComplete = (uri: string) => {
console.log('Recording saved to:', uri);
};
const handleRecordingStart = () => {
console.log('Recording started');
};
const handleRecordingStop = () => {
console.log('Recording stopped');
};
return (
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={300} // 5 minutes
onRecordingComplete={handleRecordingComplete}
onRecordingStart={handleRecordingStart}
onRecordingStop={handleRecordingStop}
/>
);
}
Installation
pnpm dlx bna-ui add audio-recorder
Usage
import { AudioRecorder } from '@/components/ui/audio-recorder';
Basic Usage
function MyComponent() {
return (
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
onRecordingComplete={(uri) => {
console.log('Recording saved to:', uri);
}}
/>
);
}
With Custom Settings
<AudioRecorder
quality='high'
maxDuration={300} // 5 minutes
showWaveform={true}
showTimer={true}
onRecordingStart={() => console.log('Recording started')}
onRecordingStop={() => console.log('Recording stopped')}
onRecordingComplete={(uri) => {
// Handle the recorded audio file
handleAudioFile(uri);
}}
/>
Low Quality for Voice Notes
<AudioRecorder
quality='low'
maxDuration={60} // 1 minute limit
showWaveform={false}
customRecordingOptions={{
...RecordingPresets.LOW_QUALITY,
bitRate: 32000, // Very low bitrate for voice
}}
/>
Examples
Default
import { AudioRecorder } from '@/components/ui/audio-recorder';
export function AudioRecorderDemo() {
const handleRecordingComplete = (uri: string) => {
console.log('Recording saved to:', uri);
};
const handleRecordingStart = () => {
console.log('Recording started');
};
const handleRecordingStop = () => {
console.log('Recording stopped');
};
return (
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={300} // 5 minutes
onRecordingComplete={handleRecordingComplete}
onRecordingStart={handleRecordingStart}
onRecordingStop={handleRecordingStop}
/>
);
}
Voice Notes
import { AudioRecorder } from '@/components/ui/audio-recorder';
export function AudioRecorderVoice() {
const handleRecordingComplete = (uri: string) => {
// Here you could add the voice note to a list or send it
console.log('Voice note saved:', uri);
};
return (
<AudioRecorder
quality='low'
showWaveform={true}
showTimer={true}
maxDuration={120} // 2 minutes for voice notes
onRecordingComplete={handleRecordingComplete}
/>
);
}
High Quality
import { AudioRecorder } from '@/components/ui/audio-recorder';
import { RecordingPresets } from 'expo-audio';
export function AudioRecorderHQ() {
const handleRecordingComplete = (uri: string) => {
console.log('HQ recording saved:', uri);
};
return (
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={1800} // 30 minutes
customRecordingOptions={{
...RecordingPresets.HIGH_QUALITY,
sampleRate: 48000,
bitRate: 192000,
numberOfChannels: 2,
}}
onRecordingComplete={handleRecordingComplete}
/>
);
}
Minimal
import { AudioRecorder } from '@/components/ui/audio-recorder';
export function AudioRecorderMinimal() {
const handleRecordingComplete = (uri: string) => {
console.log('Your audio has been recorded.', uri);
};
return (
<AudioRecorder
quality='low'
showWaveform={false}
showTimer={true}
maxDuration={60} // 1 minute
onRecordingComplete={handleRecordingComplete}
/>
);
}
Custom Styled
import { AudioRecorder } from '@/components/ui/audio-recorder';
export function AudioRecorderStyled() {
const handleRecordingComplete = (uri: string) => {
console.log('🎵 Recording Complete', uri);
};
return (
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={300}
style={{
backgroundColor: 'transparent',
borderWidth: 2,
borderColor: 'red',
borderRadius: 20,
padding: 24,
shadowColor: 'red',
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.1,
shadowRadius: 12,
elevation: 8,
}}
onRecordingComplete={handleRecordingComplete}
/>
);
}
Callbacks Recorder
import { AudioRecorder } from '@/components/ui/audio-recorder';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useState } from 'react';
export function AudioRecorderCallbacks() {
const [status, setStatus] = useState('Ready to record');
const [recordingCount, setRecordingCount] = useState(0);
const handleRecordingStart = () => {
setStatus('🔴 Recording in progress...');
console.log('Recording started');
};
const handleRecordingStop = () => {
setStatus('✅ Recording stopped');
console.log('Recording stopped');
};
const handleRecordingComplete = (uri: string) => {
setRecordingCount((prev) => prev + 1);
setStatus(`📁 Recording #${recordingCount + 1} saved`);
// Reset status after 3 seconds
setTimeout(() => setStatus('Ready to record'), 3000);
};
const getOrdinalSuffix = (num: number) => {
const lastDigit = num % 10;
const lastTwoDigits = num % 100;
if (lastTwoDigits >= 11 && lastTwoDigits <= 13) return 'th';
if (lastDigit === 1) return 'st';
if (lastDigit === 2) return 'nd';
if (lastDigit === 3) return 'rd';
return 'th';
};
return (
<View style={{ width: '100%' }}>
<Text
variant='body'
style={{ marginBottom: 16, textAlign: 'center', fontWeight: '500' }}
>
Status: {status}
</Text>
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={180} // 3 minutes
onRecordingStart={handleRecordingStart}
onRecordingStop={handleRecordingStop}
onRecordingComplete={handleRecordingComplete}
/>
{recordingCount > 0 && (
<Text variant='caption' style={{ marginTop: 12, textAlign: 'center' }}>
Total recordings: {recordingCount}
</Text>
)}
</View>
);
}
Cloud Integration Recorder
import { AudioRecorder } from '@/components/ui/audio-recorder';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';
import { ActivityIndicator } from 'react-native';
export function AudioRecorderCloud() {
const [uploading, setUploading] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const simulateCloudUpload = async (uri: string): Promise<string> => {
return new Promise((resolve) => {
setUploading(true);
setUploadProgress(0);
const interval = setInterval(() => {
setUploadProgress((prev) => {
if (prev >= 100) {
clearInterval(interval);
setUploading(false);
resolve(
`https://cloud-storage.example.com/audio/${Date.now()}.m4a`
);
return 100;
}
return prev + 10;
});
}, 200);
});
};
const handleRecordingComplete = async (uri: string) => {
try {
const cloudUrl = await simulateCloudUpload(uri);
console.log(`Recording uploaded to cloud storage!\n\nURL: ${cloudUrl}`);
setUploadProgress(0);
} catch (error) {
console.log('Failed to upload recording to cloud storage.');
setUploading(false);
setUploadProgress(0);
}
};
return (
<View style={{ width: '100%' }}>
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={600} // 10 minutes
onRecordingComplete={handleRecordingComplete}
/>
{uploading && (
<View style={{ marginTop: 16, alignItems: 'center' }}>
<ActivityIndicator size='small' />
<Text variant='caption' style={{ marginTop: 8 }}>
Uploading... {uploadProgress}%
</Text>
</View>
)}
</View>
);
}
Interview Mode Recorder
import { AudioRecorder } from '@/components/ui/audio-recorder';
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { RecordingPresets } from 'expo-audio';
import React, { useState } from 'react';
import { Alert, StyleSheet } from 'react-native';
export function AudioRecorderInterview() {
const [interviewTitle, setInterviewTitle] = useState('');
const [isRecording, setIsRecording] = useState(false);
const handleRecordingStart = () => {
setIsRecording(true);
const title = `Interview ${new Date().toLocaleDateString()}`;
setInterviewTitle(title);
};
const handleRecordingStop = () => {
setIsRecording(false);
};
const handleRecordingComplete = (uri: string) => {
Alert.alert(
'🎤 Interview Complete',
`"${interviewTitle}" has been recorded and saved.\n\nDuration: Available in file metadata\nQuality: High (48kHz, Stereo)`,
[{ text: 'Save & Exit' }]
);
};
return (
<View style={{ width: '100%' }}>
<View style={styles.interviewHeader}>
<Text variant='title' style={styles.interviewTitle}>
{interviewTitle || 'Ready for Interview'}
</Text>
<View
style={[styles.statusBadge, isRecording && styles.recordingBadge]}
>
<Text
style={[styles.statusText, isRecording && styles.recordingText]}
>
{isRecording ? '🔴 LIVE' : '⏸️ READY'}
</Text>
</View>
</View>
<AudioRecorder
quality='high'
showWaveform={true}
showTimer={true}
maxDuration={7200} // 2 hours for long interviews
customRecordingOptions={{
...RecordingPresets.HIGH_QUALITY,
sampleRate: 48000,
bitRate: 128000,
numberOfChannels: 2,
isMeteringEnabled: true,
}}
onRecordingStart={handleRecordingStart}
onRecordingStop={handleRecordingStop}
onRecordingComplete={handleRecordingComplete}
/>
<Text variant='caption' style={{ marginTop: 12, textAlign: 'center' }}>
Maximum duration: 2 hours • High quality stereo recording
</Text>
</View>
);
}
const styles = StyleSheet.create({
interviewHeader: {
marginBottom: 16,
alignItems: 'center',
},
interviewTitle: {
marginBottom: 8,
textAlign: 'center',
},
statusBadge: {
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 12,
backgroundColor: '#f3f4f6',
},
recordingBadge: {
backgroundColor: '#fef2f2',
},
statusText: {
fontSize: 12,
fontWeight: '600',
color: '#6b7280',
},
recordingText: {
color: '#dc2626',
},
});
API Reference
AudioRecorder
The main AudioRecorder component.
Prop | Type | Default | Description |
---|---|---|---|
style | ViewStyle | - | Additional styles for the recorder container. |
quality | 'high' | 'low' | 'high' | Recording quality preset. |
showWaveform | boolean | true | Whether to show real-time waveform visualization. |
showTimer | boolean | true | Whether to show the recording timer. |
maxDuration | number | - | Maximum recording duration in seconds. |
onRecordingComplete | (uri: string) => void | - | Callback fired when recording is completed and saved. |
onRecordingStart | () => void | - | Callback fired when recording starts. |
onRecordingStop | () => void | - | Callback fired when recording stops. |
customRecordingOptions | RecordingOptions | - | Custom recording options to override presets. |
Recording Quality Presets
The component includes two built-in quality presets:
High Quality
- Sample Rate: 44,100 Hz
- Bit Rate: 128,000 bps
- Channels: 2 (Stereo)
- Format: AAC
- Best for: Music, professional recordings
Low Quality
- Sample Rate: 22,050 Hz
- Bit Rate: 64,000 bps
- Channels: 1 (Mono)
- Format: AAC
- Best for: Voice notes, quick recordings
Custom Recording Options
type RecordingOptions = {
sampleRate?: number;
bitRate?: number;
numberOfChannels?: number;
format?: string;
isMeteringEnabled?: boolean;
};
Features
Real-time Waveform Visualization
The recorder shows live audio levels:
- 30 bars representing real-time audio amplitude
- Smooth animation updates every 80ms
- Uses actual microphone input when available
- Falls back to realistic simulated data
- Color changes based on recording state
Recording Controls
Intuitive recording interface:
- Record Button: Large, prominent button to start recording
- Stop Button: Clear square icon to stop recording
- Animated Feedback: Pulsing animation during recording
- Visual Indicators: Recording status with red dot
Built-in Playback
After recording, users can:
- Play/Pause: Review the recorded audio
- Seek: Navigate through the recording using waveform or progress bar
- Save: Confirm and save the recording
- Delete: Discard the recording and start over
Timer Display
Precision timing information:
- Real-time recording duration
- Centisecond accuracy (MM:SS.CC format)
- Maximum duration indicator when set
- Monospace font for consistent display
Permissions
The AudioRecorder automatically handles microphone permissions:
- Requests permission on first use
- Shows helpful error messages if denied
- Provides clear instructions for enabling permissions
- Gracefully handles permission changes
iOS Permissions
Add to your Info.plist
:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs access to the microphone to record audio.</string>
Android Permissions
Add to your AndroidManifest.xml
:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Platform Support
iOS
- Native audio recording with hardware integration
- Automatic audio session management
- Background recording support
- Hardware control integration
Android
- Optimized audio capture
- Proper lifecycle management
- Background recording with proper permissions
- Audio focus handling
Web
- MediaRecorder API integration
- Browser compatibility fallbacks
- Microphone access handling
Accessibility
The AudioRecorder follows accessibility standards:
- Screen reader announcements for recording state
- Clear button labels and roles
- Keyboard navigation support
- High contrast mode support
- Proper focus management
Performance Optimization
The component is optimized for:
- Memory Efficiency: Proper cleanup of audio resources
- Battery Life: Efficient audio processing and minimal background activity
- Storage: Compressed audio formats to minimize file size
- Responsiveness: Non-blocking UI during recording operations
Theming
Automatic theme integration:
- Recording button uses destructive/red theme color
- Background adapts to secondary theme color
- Text colors follow theme hierarchy
- Supports light and dark modes
- Waveform colors match theme accent
Advanced Usage
Custom Recording Configuration
<AudioRecorder
customRecordingOptions={{
sampleRate: 48000,
bitRate: 192000,
numberOfChannels: 2,
format: 'wav',
isMeteringEnabled: true,
}}
onRecordingComplete={(uri) => {
// Handle high-quality WAV file
uploadAudioFile(uri);
}}
/>
Integration with Cloud Storage
function CloudRecorder() {
const handleRecordingComplete = async (uri: string) => {
try {
// Upload to cloud storage
const downloadUrl = await uploadToFirebase(uri);
// Save reference in database
await saveAudioRecord({
url: downloadUrl,
timestamp: new Date(),
duration: recordingDuration,
});
Alert.alert('Success', 'Recording saved to cloud!');
} catch (error) {
Alert.alert('Error', 'Failed to save recording');
}
};
return (
<AudioRecorder
quality='high'
maxDuration={600} // 10 minutes
onRecordingComplete={handleRecordingComplete}
/>
);
}
Voice Note Integration
<AudioRecorder
quality='low'
maxDuration={120} // 2 minutes for voice notes
showWaveform={false} // Simplified UI
onRecordingComplete={(uri) => {
// Add to voice notes collection
addVoiceNote({
audioUri: uri,
createdAt: new Date(),
transcription: null, // Add speech-to-text later
});
}}
/>
Troubleshooting
Permission Issues
- Check that microphone permissions are granted
- Verify Info.plist/AndroidManifest.xml configuration
- Test on physical device (simulator may have limitations)
Audio Quality Problems
- Ensure device microphone is working
- Check for background noise interference
- Try different quality presets
- Verify adequate storage space
Recording Interruptions
- Handle phone calls and other audio interruptions
- Implement proper audio session management
- Save partial recordings when interrupted
Performance Issues
- Reduce waveform update frequency on older devices
- Use lower quality settings for better performance
- Disable real-time waveform on low-end devices
On This Page
InstallationUsageBasic UsageWith Custom SettingsLow Quality for Voice NotesExamplesDefaultVoice NotesHigh QualityMinimalCustom StyledCallbacks RecorderCloud Integration RecorderInterview Mode RecorderAPI ReferenceAudioRecorderRecording Quality PresetsHigh QualityLow QualityCustom Recording OptionsFeaturesReal-time Waveform VisualizationRecording ControlsBuilt-in PlaybackTimer DisplayPermissionsiOS PermissionsAndroid PermissionsPlatform SupportiOSAndroidWebAccessibilityPerformance OptimizationThemingAdvanced UsageCustom Recording ConfigurationIntegration with Cloud StorageVoice Note IntegrationTroubleshootingPermission IssuesAudio Quality ProblemsRecording InterruptionsPerformance Issues