Skip to content

Advanced Usage

Advanced patterns and techniques for the Lingu React Native SDK.

Custom Chat UI

Build your own chat interface using the useLinguChat hook:

tsx
import { useState, useEffect, useRef } from 'react';
import {
  View,
  Text,
  TextInput,
  FlatList,
  TouchableOpacity,
  StyleSheet,
  KeyboardAvoidingView,
  Platform,
} from 'react-native';
import { useLinguChat } from '@lingu/react-native-sdk';

export function CustomChatScreen() {
  const [input, setInput] = useState('');
  const flatListRef = useRef<FlatList>(null);
  
  const {
    messages,
    isLoading,
    sendMessage,
    startSession,
    endSession,
    isSessionActive,
  } = useLinguChat({
    apiKey: process.env.EXPO_PUBLIC_LINGU_API_KEY!,
  });

  useEffect(() => {
    startSession();
    return () => {
      endSession();
    };
  }, []);

  useEffect(() => {
    // Auto-scroll to bottom on new messages
    if (messages.length > 0) {
      flatListRef.current?.scrollToEnd();
    }
  }, [messages]);

  const handleSend = async () => {
    if (!input.trim() || isLoading) return;
    const text = input;
    setInput('');
    await sendMessage(text);
  };

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : undefined}
    >
      <FlatList
        ref={flatListRef}
        data={messages}
        keyExtractor={(item) => item.id}
        contentContainerStyle={styles.messageList}
        renderItem={({ item }) => (
          <View style={[
            styles.messageBubble,
            item.sender === 'user' ? styles.userBubble : styles.botBubble,
          ]}>
            <Text style={[
              styles.messageText,
              item.sender === 'user' && styles.userText,
            ]}>
              {item.text}
            </Text>
          </View>
        )}
      />
      
      {isLoading && (
        <View style={styles.typingIndicator}>
          <Text style={styles.typingText}>AI is typing...</Text>
        </View>
      )}
      
      <View style={styles.inputContainer}>
        <TextInput
          value={input}
          onChangeText={setInput}
          placeholder="Type a message..."
          style={styles.input}
          multiline
        />
        <TouchableOpacity
          onPress={handleSend}
          style={[styles.sendButton, !input.trim() && styles.sendButtonDisabled]}
          disabled={!input.trim() || isLoading}
        >
          <Text style={styles.sendButtonText}>Send</Text>
        </TouchableOpacity>
      </View>
    </KeyboardAvoidingView>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#fff' },
  messageList: { padding: 16 },
  messageBubble: { maxWidth: '80%', padding: 12, borderRadius: 16, marginVertical: 4 },
  userBubble: { alignSelf: 'flex-end', backgroundColor: '#3b82f6' },
  botBubble: { alignSelf: 'flex-start', backgroundColor: '#f3f4f6' },
  messageText: { fontSize: 16 },
  userText: { color: '#fff' },
  typingIndicator: { padding: 16 },
  typingText: { color: '#6b7280', fontStyle: 'italic' },
  inputContainer: { flexDirection: 'row', padding: 12, borderTopWidth: 1, borderTopColor: '#e5e7eb' },
  input: { flex: 1, borderWidth: 1, borderColor: '#d1d5db', borderRadius: 20, paddingHorizontal: 16, paddingVertical: 8, maxHeight: 100 },
  sendButton: { marginLeft: 8, backgroundColor: '#3b82f6', borderRadius: 20, paddingHorizontal: 16, justifyContent: 'center' },
  sendButtonDisabled: { backgroundColor: '#9ca3af' },
  sendButtonText: { color: '#fff', fontWeight: '600' },
});

Analytics Integration

Track chat events with your analytics provider:

tsx
import analytics from '@segment/analytics-react-native';

<LinguChat
  apiKey="your-api-key"
  onSessionStart={(sessionId) => {
    analytics.track('Chat Started', { sessionId });
  }}
  onSessionEnd={(sessionId) => {
    analytics.track('Chat Ended', { sessionId });
  }}
  onMessageSent={(message) => {
    analytics.track('Chat Message Sent', {
      message,
      messageLength: message.length,
    });
  }}
  onMessageReceived={(message) => {
    analytics.track('Chat Response Received', {
      response: message.substring(0, 100), // First 100 chars
    });
  }}
/>

React Navigation

Open chat as a modal screen:

tsx
// navigation/types.ts
export type RootStackParamList = {
  Home: undefined;
  Chat: undefined;
};

// screens/ChatScreen.tsx
import { useNavigation } from '@react-navigation/native';
import { LinguChat } from '@lingu/react-native-sdk';

export function ChatScreen() {
  const navigation = useNavigation();
  
  return (
    <View style={{ flex: 1 }}>
      <LinguChat
        apiKey="your-api-key"
        showBackButton={true}
        onBackPress={() => navigation.goBack()}
      />
    </View>
  );
}

// App.tsx
<Stack.Screen
  name="Chat"
  component={ChatScreen}
  options={{
    presentation: 'modal',
    headerShown: false,
  }}
/>

Expo Integration

Environment Variables

bash
# .env
EXPO_PUBLIC_LINGU_API_KEY=lingu_abc123
tsx
<LinguChat
  apiKey={process.env.EXPO_PUBLIC_LINGU_API_KEY!}
/>

Development Build

For production, create a development build:

bash
npx expo prebuild
npx expo run:ios
# or
npx expo run:android

EAS Build

bash
eas build --platform all

Conditional Rendering

Show Only When Authenticated

tsx
function App() {
  const { user, isAuthenticated } = useAuth();
  
  return (
    <View style={{ flex: 1 }}>
      {/* Your app content */}
      
      {isAuthenticated && (
        <LinguChat
          apiKey="your-api-key"
          onSessionStart={(id) => {
            // Associate session with user
            api.associateUser(id, user.id);
          }}
        />
      )}
    </View>
  );
}

Show on Specific Screens

tsx
function ProductScreen({ route }) {
  const showChat = route.params?.showSupport ?? false;
  
  return (
    <View style={{ flex: 1 }}>
      {/* Product content */}
      
      {showChat && (
        <LinguChat apiKey="your-api-key" autoOpen={true} />
      )}
    </View>
  );
}

Session Persistence

Messages are automatically persisted using AsyncStorage. To clear history:

typescript
import AsyncStorage from '@react-native-async-storage/async-storage';

async function clearChatHistory() {
  await AsyncStorage.removeItem('lingu_messages');
  await AsyncStorage.removeItem('lingu_session');
}

Error Handling

Wrap the component with error boundary:

tsx
import { ErrorBoundary } from 'react-error-boundary';

function ChatErrorFallback({ error, resetErrorBoundary }) {
  return (
    <View style={{ padding: 20 }}>
      <Text>Chat temporarily unavailable</Text>
      <Button title="Try Again" onPress={resetErrorBoundary} />
    </View>
  );
}

function App() {
  return (
    <ErrorBoundary FallbackComponent={ChatErrorFallback}>
      <LinguChat apiKey="your-api-key" />
    </ErrorBoundary>
  );
}

Performance Tips

  1. Lazy Load: Only mount LinguChat when needed
  2. Memoize Callbacks: Use useCallback for event handlers
  3. Limit Re-renders: Don't pass new object references as props
  4. Clean Up: Unmount the component when navigating away
tsx
const handleSessionStart = useCallback((id: string) => {
  console.log('Session:', id);
}, []);

<LinguChat
  apiKey="your-api-key"
  onSessionStart={handleSessionStart}
/>

Powered by Lingu