Skip to content

Expo and React Native

The widget embeds in an Expo or bare React Native app via react-native-webview. Full feature parity with the web widget, typically about a day of integration work.

Setup

bash
npx expo install react-native-webview

Component

tsx
import { useMemo } from 'react';
import { ActivityIndicator, StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';

const WIDGET_ORIGIN = 'https://widget.inviaro.com';
const EMBED_URL = `${WIDGET_ORIGIN}/embed`;

export function InviaroChat({ credentials }) {
  const injected = useMemo(() => {
    const settings = {
      key: process.env.EXPO_PUBLIC_INVIARO_KEY,
      origin: WIDGET_ORIGIN,
      mode: 'embed',
      ...(credentials.userId
        ? {
            userId: credentials.userId,
            userHash: credentials.userHash,
            userEmail: credentials.userEmail,
            userName: credentials.userName
          }
        : {})
    };
    return `window.InviaroSettings = ${JSON.stringify(settings)}; true;`;
  }, [credentials]);

  return (
    <View style={{ flex: 1 }}>
      <WebView
        source={{ uri: EMBED_URL }}
        injectedJavaScriptBeforeContentLoaded={injected}
        originWhitelist={[WIDGET_ORIGIN]}
        javaScriptEnabled
        domStorageEnabled
        keyboardDisplayRequiresUserAction={false}
        startInLoadingState
        renderLoading={() => (
          <View style={styles.loading}>
            <ActivityIndicator size="large" />
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  loading: { flex: 1, alignItems: 'center', justifyContent: 'center' }
});

Auth hook

tsx
import { useEffect, useState } from 'react';

export function useInviaroCredentials() {
  const [creds, setCreds] = useState({
    userId: null,
    userHash: null,
    userEmail: null,
    userName: null
  });

  useEffect(() => {
    fetch('https://yourapi.com/api/me/widget-credentials', {
      headers: { Authorization: `Bearer ${getToken()}` }
    })
      .then((r) => r.json())
      .then(setCreds)
      .catch(() => {
        // Anonymous fallback on auth failure.
      });
  }, []);

  return creds;
}

Backend endpoint

Your backend computes user_hash server-side. See Identity verification for code samples.

js
app.get('/api/me/widget-credentials', requireAuth, (req, res) => {
  const user = req.user;
  res.json({
    userId: user.id,
    userHash: hmacSha256(user.id, process.env.INVIARO_WIDGET_SECRET),
    userEmail: user.email,
    userName: user.name
  });
});

Security

The widget_secret lives only on your server. Never bundle it into the Expo app.

Behavior

What works inside the WebView exactly like the web widget:

  • Polling and realtime delivery.
  • Pre-chat form with localStorage persistence.
  • HMAC identity verification.
  • Light and dark theme.
  • Localization.
  • CSAT thumbs.
  • Privacy modes and consent gate.

What is slightly different:

  • Browser-style notifications may not work in the WebView.
  • Keyboard handling requires keyboardDisplayRequiresUserAction={false} to allow programmatic focus on the composer.
  • WebView storage is per-origin and persists across app launches.