React Native (@frontmail/react-native)
Mit @frontmail/react-native sendet eine iOS- oder Android-App E-Mails über Ihre Vorlagen mit dem
öffentlichen Schlüssel – ganz ohne eigenes Backend. Es funktioniert mit Expo und mit Bare React
Native.
Installation
Abschnitt betitelt „Installation“Expo:
npx expo install @frontmail/react-native @react-native-async-storage/async-storage react-native-webviewBare React Native:
npm install @frontmail/react-native @react-native-async-storage/async-storage react-native-webviewnpx pod-installDie beiden zusätzlichen Pakete sind optionale Peer-Dependencies:
| Paket | Benötigt für |
|---|---|
@react-native-async-storage/async-storage |
limitRate, das App-Neustarts übersteht |
react-native-webview |
<TurnstileWebView> |
Mobile Apps erlauben
Abschnitt betitelt „Mobile Apps erlauben“Native Apps senden keinen Origin- oder Referer-Header, daher werden Anfragen aus der App (egal ob
Sie erlaubte Websites eingetragen haben oder nicht) mit
403 origin_not_allowed abgelehnt, bis Sie im Dashboard
Sicherheit → Mobile Apps → Mobile Apps erlauben einschalten und auf Speichern klicken. Siehe
Sicherheitseinstellungen.
Anfragen von Websites werden weiterhin mit der Liste abgeglichen – der Schalter schwächt also den Schutz Ihrer Webformulare nicht. Er ist auch bei leerer Liste erforderlich.
Provider und useSendEmail
Abschnitt betitelt „Provider und useSendEmail“Umschließen Sie die App mit FrontmailProvider und senden Sie mit useSendEmail. Halten Sie das
Optionsobjekt stabil – definieren Sie es auf Modulebene oder mit useMemo:
import { useState } from 'react';import { Button, Text, TextInput, View } from 'react-native';import { FrontmailProvider, useSendEmail } from '@frontmail/react-native';
const frontmailOptions = { publicKey: 'pk_4f2a…', limitRate: { id: 'contact', throttle: 30_000 } };
export default function App() { return ( <FrontmailProvider options={frontmailOptions}> <ContactScreen /> </FrontmailProvider> );}
function ContactScreen() { const { send, status, error } = useSendEmail('svc_01J9…', 'tpl_contact'); const [email, setEmail] = useState(''); const [message, setMessage] = useState('');
return ( <View> <TextInput value={email} onChangeText={setEmail} keyboardType="email-address" autoCapitalize="none" /> <TextInput value={message} onChangeText={setMessage} multiline /> <Button title="Senden" disabled={status === 'sending'} onPress={() => send({ email, message })} /> {status === 'sent' && <Text>Vielen Dank!</Text>} {status === 'held' && <Text>Erhalten – wird in Kürze zugestellt.</Text>} {error && <Text>{error.message}</Text>} </View> );}options akzeptiert publicKey, apiUrl, retry, limitRate, storageProvider und blockList.
Ein privateKey wird nicht akzeptiert. Alternativ übergeben Sie einen vorhandenen Client:
<FrontmailProvider client={client}>.
useSendEmail(serviceId, templateId) liefert { send, getStatus, status, error, result, reset }.
status durchläuft idle → sending → sent | held | error. send(params, options?) liefert
das Ergebnis oder bei einem Fehler undefined (der Fehler steht in error) – es wird nie abgelehnt,
Sie brauchen also kein try/catch. reset() setzt den Hook auf idle zurück.
Um in einer Sprache der Vorlage zu senden, übergeben Sie locale in
den Optionen: send({ email, message }, { locale: 'de' }) – zum Beispiel die Sprache des Geräts aus
Intl.DateTimeFormat().resolvedOptions().locale. Unbekannte Sprachen fallen auf dieselbe
Basissprache zurück, danach auf die Standardsprache der Vorlage; result.locale ist die verwendete
Sprache.
Jede Anfrage trägt den Header X-Frontmail-Client: @frontmail/react-native/<version>.
Turnstile
Abschnitt betitelt „Turnstile“Verlangt die Vorlage Turnstile, rendern Sie <TurnstileWebView> und
übergeben den Token an send. Einen Site-Key brauchen Sie nicht: Standardmäßig verwendet die
Komponente den gemeinsamen mobilen Schlüssel von Frontmail. Sie holt ihn von
GET /v1/public-config der im Provider eingestellten API
(zwischengespeichert, also nur einmal geladen) und zeigt das Cloudflare-Widget in
react-native-webview als Inline-HTML-Seite mit der URL https://mobile.frontmail.dev an – von
dieser Adresse wird nichts geladen.
import { useRef, useState } from 'react';import { Button } from 'react-native';import { TurnstileWebView, useSendEmail } from '@frontmail/react-native';import type { TurnstileWebViewHandle } from '@frontmail/react-native';
function ContactScreen() { const { send, status } = useSendEmail('svc_01J9…', 'tpl_contact'); const turnstile = useRef<TurnstileWebViewHandle>(null); const [token, setToken] = useState<string>();
const onSubmit = async () => { await send({ email, message }, { turnstileToken: token }); // Tokens sind nur einmal gültig – für den nächsten Versuch einen neuen holen. setToken(undefined); turnstile.current?.reset(); };
return ( <> {/* …Formularfelder… */} <TurnstileWebView ref={turnstile} onToken={setToken} onExpire={() => setToken(undefined)} onError={(e) => console.warn(e.message)} /> <Button title="Senden" disabled={!token || status === 'sending'} onPress={onSubmit} /> </> );}Props: onToken und optional siteKey, baseUrl, onError, onExpire, theme
(auto | light | dark), size (normal | compact | flexible), action, language,
style sowie webViewProps (werden an die WebView durchgereicht).
Die WebView ist auf das Widget beschränkt: Sie lädt nur die Inline-Seite (baseUrl) und die
Challenge von Cloudflare, Links im Widget öffnen sich im Systembrowser, und nur Nachrichten der
Widget-Seite erreichen Ihre Callbacks. webViewProps kann source, originWhitelist, den
Navigationsschutz, onMessage und die Einstellungen für JavaScript und Dateizugriff nicht
überschreiben.
App-Anfragen haben keinen Origin-Header, daher prüft Frontmail ihre Tokens mit seinem gemeinsamen
mobilen Secret. Eigene Turnstile-Schlüssel brauchen Sie dafür nicht – mit aktiviertem
Mobile Apps erlauben können Sie das CAPTCHA für die Vorlagen der App einschalten, auch wenn Ihre
Organisation noch keine Schlüssel hat.
Eigener Site-Key
Abschnitt betitelt „Eigener Site-Key“Um stattdessen Ihr eigenes Turnstile-Widget zu verwenden, übergeben Sie siteKey und baseUrl:
<TurnstileWebView ref={turnstile} siteKey="YOUR_TURNSTILE_SITE_KEY" baseUrl="https://example.com" onToken={setToken}/>Das SDK sendet Tokens aus diesem Widget dann automatisch mit turnstile_key: "org", und Frontmail
prüft sie mit dem geheimen Schlüssel aus Sicherheit → Bot-Schutz (Turnstile).
Ein Token ist etwa 5 Minuten gültig und kann einmal verwendet werden: Rufen Sie nach jedem
Versand ref.current.reset() auf, ob erfolgreich oder nicht.
limitRate und Speicher
Abschnitt betitelt „limitRate und Speicher“limitRate drosselt das Senden auf dem Gerät, z. B. auf höchstens eine Nachricht pro 30 Sekunden.
In React Native gibt es kein location.pathname, setzen Sie daher immer limitRate.id:
import AsyncStorage from '@react-native-async-storage/async-storage';import { asyncStorageProvider } from '@frontmail/react-native';
const frontmailOptions = { publicKey: 'pk_4f2a…', limitRate: { id: 'contact', throttle: 30_000 }, storageProvider: asyncStorageProvider(AsyncStorage),};Ohne storageProvider nutzt das SDK AsyncStorage, sofern installiert; andernfalls hält es das Limit
nur im Speicher (es wird beim Neustart der App zurückgesetzt) und gibt in der Entwicklung eine Warnung
aus. Mit memoryStorageProvider() wählen Sie den Speicher im Arbeitsspeicher ausdrücklich.
limitRate schützt nur vor versehentlich wiederholtem Tippen – der eigentliche Schutz liegt auf dem
Server (siehe Sicherheit).
getStatus
Abschnitt betitelt „getStatus“getStatus() aus dem Hook liest den Zustellstatus der zuletzt angenommenen Nachricht. Für alles
andere liefert useFrontmail() den Client aus dem Provider (send, getStatus):
import { useFrontmail } from '@frontmail/react-native';
const client = useFrontmail();const status = await client.getStatus(messageId, { token });Wiederholungen und Idempotenz
Abschnitt betitelt „Wiederholungen und Idempotenz“Netzwerkfehler, 5xx und 429 werden automatisch wiederholt, und alle Versuche einer Sendung
verwenden denselben Idempotenzschlüssel – eine Wiederholung sendet die E-Mail also nie doppelt. Die
Schlüssel sind UUID v4 – das SDK nutzt crypto.randomUUID und greift unter Hermes, das diese
Funktion nicht bietet, auf einen eigenen Generator zurück. Siehe
Wiederholungen und Idempotenz.
Typisierte Parameter
Abschnitt betitelt „Typisierte Parameter“Mit generierten Typen (npx frontmail types erweitert
@frontmail/react-native) prüft useSendEmail('svc_…', 'tpl_contact').send({...}) die Parameter
dieser Vorlage auf Typfehler.
error ist ein FrontmailError mit code, status und message. Wie Sie mit den einzelnen Codes
umgehen, steht unter Fehlerbehandlung.
Sicherheit
Abschnitt betitelt „Sicherheit“- Bauen Sie in eine App immer nur den öffentlichen Schlüssel (
pk_…) ein. Alles in einem App-Bundle lässt sich extrahieren, ein privater Schlüssel darin ist also so gut wie veröffentlicht. Siehe Öffentliche und private Schlüssel. - Mit aktiviertem Mobile Apps erlauben werden Anfragen ohne
Origin-Header von überall angenommen – jedes Skript, das Ihren öffentlichen Schlüssel kennt, kann sie senden. Verlassen Sie sich auf das Rate Limit pro IP, Turnstile für jede Vorlage, die die App verwendet, und die Blockliste.
Beispielprojekt: examples/react-native.