SDK
React Native SDK
Official React Native client library for Nur's Voice AI platform. Build native iOS and Android voice apps with React.
Installation
Requires React Native 0.70+ and React 18+. Install via npm or yarn:
1npm install @nur/react-native
For iOS, run pod install:
1cd ios && pod install && cd ..
Quick Start
Create a client and generate your first speech with hooks:
1import React from 'react';
2import { View, Button, Text } from 'react-native';
3import { NurProvider, useNur } from '@nur/react-native';
4
5function VoiceApp() {
6 const { tts } = useNur();
7 const [status, setStatus] = React.useState('');
8
9 const generateSpeech = async () => {
10 try {
11 setStatus('Generating...');
12
13 // Generate speech from text
14 const audio = await tts.generate({
15 text: 'Hello from React Native! This is Nur\'s voice AI.',
16 voiceId: 'rachel_v2',
17 language: 'en',
18 });
19
20 // Audio plays automatically on mobile
21 setStatus(`Generated ${audio.duration.toFixed(2)}s of audio`);
22 } catch (error) {
23 setStatus(`Error: ${error.message}`);
24 }
25 };
26
27 return (
28 <View style={{ padding: 20 }}>
29 <Button title="Generate Speech" onPress={generateSpeech} />
30 <Text>{status}</Text>
31 </View>
32 );
33}
34
35export default function App() {
36 return (
37 <NurProvider apiKey="nur_your_api_key_here">
38 <VoiceApp />
39 </NurProvider>
40 );
41}
Authentication
Configure your API key via the NurProvider or environment variables:
1import { NurProvider, NurClient } from '@nur/react-native';
2
3// Option 1: Using NurProvider (recommended for React components)
4export default function App() {
5 return (
6 <NurProvider
7 apiKey="nur_your_api_key_here"
8 config={{
9 baseUrl: 'https://api.nur.ai',
10 timeout: 60000,
11 maxRetries: 3,
12 }}
13 >
14 <YourApp />
15 </NurProvider>
16 );
17}
18
19// Option 2: Direct client instantiation
20const client = new NurClient({
21 apiKey: 'nur_your_api_key_here',
22 baseUrl: 'https://api.nur.ai',
23 timeout: 60000,
24 maxRetries: 3,
25});
26
27// Option 3: Using environment variables (.env)
28// NUR_API_KEY=nur_your_api_key_here
29const client = new NurClient(); // Reads from env
Text to Speech
Generate natural-sounding speech with React hooks and native audio:
1import React from 'react';
2import { View, TextInput, Button, ActivityIndicator } from 'react-native';
3import { useNur } from '@nur/react-native';
4
5function TTSComponent() {
6 const { tts } = useNur();
7 const [text, setText] = React.useState('');
8 const [loading, setLoading] = React.useState(false);
9
10 // Basic generation
11 const generateBasic = async () => {
12 setLoading(true);
13 try {
14 const audio = await tts.generate({
15 text,
16 voiceId: 'rachel_v2',
17 });
18 // Audio plays automatically
19 } finally {
20 setLoading(false);
21 }
22 };
23
24 // With advanced options
25 const generateAdvanced = async () => {
26 const audio = await tts.generate({
27 text,
28 voiceId: 'rachel_v2',
29 language: 'en',
30 speed: 1.0,
31 pitch: 1.0,
32 outputFormat: 'mp3',
33 sampleRate: 24000,
34 enableSSML: true,
35 });
36 };
37
38 // Streaming generation
39 const streamAudio = async () => {
40 const stream = tts.stream({
41 text: 'Streaming long-form content...',
42 voiceId: 'rachel_v2',
43 });
44
45 for await (const chunk of stream) {
46 console.log(`Received ${chunk.length} bytes`);
47 // Chunks play automatically as they arrive
48 }
49 };
50
51 return (
52 <View style={{ padding: 20 }}>
53 <TextInput
54 value={text}
55 onChangeText={setText}
56 placeholder="Enter text to speak..."
57 multiline
58 style={{
59 borderWidth: 1,
60 padding: 10,
61 marginBottom: 10,
62 minHeight: 100
63 }}
64 />
65 <Button
66 title={loading ? 'Generating...' : 'Generate Speech'}
67 onPress={generateBasic}
68 disabled={loading || !text}
69 />
70 {loading && <ActivityIndicator style={{ marginTop: 10 }} />}
71 </View>
72 );
73}
74
75// Using hooks for audio control
76import { useAudio } from '@nur/react-native';
77
78function AudioPlayer() {
79 const { play, pause, stop, isPlaying } = useAudio();
80
81 const handleGenerate = async () => {
82 const audio = await tts.generate({
83 text: 'Hello world',
84 voiceId: 'rachel_v2',
85 autoPlay: false, // Don't play automatically
86 });
87
88 // Manual control
89 await play(audio);
90 };
91
92 return (
93 <View>
94 <Button title={isPlaying ? 'Pause' : 'Play'}
95 onPress={isPlaying ? pause : handleGenerate} />
96 <Button title="Stop" onPress={stop} />
97 </View>
98 );
99}
Speech to Text
High-accuracy transcription with microphone hooks:
1import React from 'react';
2import { View, Button, Text, PermissionsAndroid, Platform } from 'react-native';
3import { useNur, useMicrophone } from '@nur/react-native';
4
5function STTComponent() {
6 const { stt } = useNur();
7 const [transcript, setTranscript] = React.useState('');
8
9 // Request microphone permission
10 const requestPermission = async () => {
11 if (Platform.OS === 'android') {
12 await PermissionsAndroid.request(
13 PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
14 );
15 }
16 };
17
18 React.useEffect(() => {
19 requestPermission();
20 }, []);
21
22 // Real-time transcription with microphone hook
23 const {
24 startRecording,
25 stopRecording,
26 isRecording,
27 transcript: liveTranscript,
28 } = useMicrophone({
29 language: 'en',
30 timestamps: true,
31 onResult: (result) => {
32 if (result.isFinal) {
33 setTranscript((prev) => prev + ' ' + result.text);
34 }
35 },
36 });
37
38 // File transcription
39 const transcribeFile = async () => {
40 const result = await stt.transcribe({
41 file: 'path/to/audio.mp3',
42 language: 'en',
43 speakerDiarization: true,
44 timestamps: true,
45 });
46
47 console.log(result.text);
48
49 // Access segments
50 result.segments.forEach((segment) => {
51 console.log(`[Speaker ${segment.speaker}] ${segment.text}`);
52 console.log(` Time: ${segment.start}s - ${segment.end}s`);
53 });
54 };
55
56 return (
57 <View style={{ padding: 20 }}>
58 <Button
59 title={isRecording ? 'Stop Recording' : 'Start Recording'}
60 onPress={isRecording ? stopRecording : startRecording}
61 color={isRecording ? 'red' : 'blue'}
62 />
63 <Text style={{ marginTop: 20, fontSize: 16 }}>
64 Live: {liveTranscript}
65 </Text>
66 <Text style={{ marginTop: 10, fontSize: 14, color: '#666' }}>
67 Final: {transcript}
68 </Text>
69 <Button
70 title="Transcribe File"
71 onPress={transcribeFile}
72 style={{ marginTop: 20 }}
73 />
74 </View>
75 );
76}
Voice Agents
Build real-time conversational AI with React hooks:
1import React from 'react';
2import { View, Button, FlatList, Text } from 'react-native';
3import { useVoiceAgent } from '@nur/react-native';
4
5function VoiceAgentComponent() {
6 const [messages, setMessages] = React.useState([]);
7
8 const {
9 startSession,
10 endSession,
11 isActive,
12 isSpeaking,
13 } = useVoiceAgent({
14 voiceId: 'rachel_v2',
15 language: 'en',
16 systemPrompt: 'You are a helpful assistant.',
17 temperature: 0.7,
18 maxTokens: 150,
19 onTranscript: (text) => {
20 setMessages((prev) => [...prev, { role: 'user', text }]);
21 },
22 onAgentResponse: (text) => {
23 setMessages((prev) => [...prev, { role: 'agent', text }]);
24 },
25 onError: (error) => {
26 console.error('Voice agent error:', error);
27 },
28 });
29
30 return (
31 <View style={{ flex: 1, padding: 20 }}>
32 <FlatList
33 data={messages}
34 keyExtractor={(item, index) => index.toString()}
35 renderItem={({ item }) => (
36 <View
37 style={{
38 padding: 10,
39 marginVertical: 5,
40 backgroundColor: item.role === 'user' ? '#E3F2FD' : '#F1F8E9',
41 borderRadius: 8,
42 }}
43 >
44 <Text style={{ fontWeight: 'bold' }}>
45 {item.role === 'user' ? 'You' : 'Agent'}
46 </Text>
47 <Text>{item.text}</Text>
48 </View>
49 )}
50 />
51 <View style={{ marginTop: 20 }}>
52 <Button
53 title={isActive ? 'End Conversation' : 'Start Conversation'}
54 onPress={isActive ? endSession : startSession}
55 color={isActive ? 'red' : 'green'}
56 />
57 {isSpeaking && (
58 <Text style={{ textAlign: 'center', marginTop: 10 }}>
59 Agent is speaking...
60 </Text>
61 )}
62 </View>
63 </View>
64 );
65}
66
67// Advanced usage with manual control
68function AdvancedVoiceAgent() {
69 const { voiceAgent } = useNur();
70 const [session, setSession] = React.useState(null);
71
72 const createSession = async () => {
73 const newSession = await voiceAgent.createSession({
74 voiceId: 'rachel_v2',
75 systemPrompt: 'You are a helpful assistant.',
76 });
77
78 setSession(newSession);
79
80 // Handle responses
81 for await (const response of newSession.receive()) {
82 switch (response.type) {
83 case 'transcript':
84 console.log('User:', response.transcript.text);
85 break;
86 case 'agentText':
87 console.log('Agent:', response.agentText);
88 break;
89 case 'audio':
90 // Audio plays automatically
91 break;
92 }
93 }
94 };
95
96 return (
97 <View>
98 <Button
99 title="Create Session"
100 onPress={createSession}
101 />
102 </View>
103 );
104}
Configuration Options
All available client configuration options:
| Option | Type | Default | Description |
|---|---|---|---|
| apiKey | string | NUR_API_KEY | Your API key |
| baseUrl | string | https://api.nur.ai | API base URL |
| timeout | number | 30000 | Request timeout (ms) |
| maxRetries | number | 3 | Maximum retry attempts |
| retryDelay | number | 1000 | Delay between retries (ms) |
| autoPlay | boolean | true | Auto-play generated audio |
Error Handling
The SDK provides typed errors and error boundaries:
1import {
2 NurError,
3 RateLimitError,
4 AuthenticationError,
5 NurErrorBoundary
6} from '@nur/react-native';
7
8// Try-catch error handling
9async function generateSpeech() {
10 try {
11 const audio = await tts.generate({
12 text: 'Hello world',
13 voiceId: 'rachel_v2',
14 });
15 } catch (error) {
16 if (error instanceof RateLimitError) {
17 console.log(`Rate limited. Retry after ${error.retryAfter}s`);
18 setTimeout(() => {
19 // Retry request...
20 }, error.retryAfter * 1000);
21 } else if (error instanceof AuthenticationError) {
22 console.error('Authentication failed: check your API key');
23 } else if (error instanceof NurError) {
24 console.error(`API error [${error.statusCode}]: ${error.message}`);
25 } else {
26 console.error('Unexpected error:', error);
27 }
28 }
29}
30
31// Using error boundary
32function App() {
33 return (
34 <NurErrorBoundary
35 fallback={(error) => (
36 <View style={{ padding: 20 }}>
37 <Text style={{ color: 'red' }}>Error: {error.message}</Text>
38 <Button title="Retry" onPress={() => window.location.reload()} />
39 </View>
40 )}
41 >
42 <YourApp />
43 </NurErrorBoundary>
44 );
45}
46
47// Hook-based error handling
48import { useError } from '@nur/react-native';
49
50function Component() {
51 const { error, clearError } = useError();
52
53 if (error) {
54 return (
55 <View>
56 <Text>Error: {error.message}</Text>
57 <Button title="Dismiss" onPress={clearError} />
58 </View>
59 );
60 }
61
62 return <YourComponent />;
63}