A modern React Native wrapper for Typeform Embed SDK
npm install react-native-typeformA modern React Native wrapper for embedding Typeform forms in your mobile app.



- Full TypeScript support - Complete type definitions for all props and events
- Two embed modes - Inline widget or modal popup
- Event callbacks - onReady, onSubmit, onClose, onQuestionChanged, onHeightChanged
- Customizable - Hidden fields, tracking, styling options
- Custom hook - useTypeform for easy state management
- Expo compatible - Works with Expo managed workflow
- Modern API - Uses the latest Typeform Embed SDK
``bashUsing npm
npm install react-native-typeform react-native-webview
$3
`bash
npx expo install react-native-typeform react-native-webview
`$3
`bash
cd ios && pod install
`Quick Start
$3
`tsx
import { TypeformWidget } from 'react-native-typeform';function SurveyScreen() {
return (
formId="abc123"
onSubmit={(event) => {
console.log('Form submitted!', event.responseId);
}}
style={{ flex: 1 }}
/>
);
}
`$3
`tsx
import { useState } from 'react';
import { Button } from 'react-native';
import { TypeformPopup } from 'react-native-typeform';function App() {
const [visible, setVisible] = useState(false);
return (
<>
formId="abc123"
visible={visible}
onDismiss={() => setVisible(false)}
onSubmit={(event) => {
console.log('Submitted:', event.responseId);
setVisible(false);
}}
/>
>
);
}
`$3
`tsx
import { Button, Text } from 'react-native';
import { TypeformPopup, useTypeform } from 'react-native-typeform';function App() {
const {
open,
popupProps,
isSubmitted,
responseId,
} = useTypeform({
onSubmit: (e) => console.log('Submitted:', e.responseId),
autoCloseOnSubmit: true,
});
return (
<>
{isSubmitted && (
Thank you! Response ID: {responseId}
)}
>
);
}
`Getting Your Form ID
You can find your form ID from the Typeform URL:
- Public URL:
https://form.typeform.com/to/
- Admin URL: https://admin.typeform.com/form/API Reference
$3
Inline embed component for displaying forms within your app's layout.
`tsx
formId="abc123" // Required: Your Typeform form ID // Callbacks
onReady={() => {}} // Form loaded successfully
onSubmit={(e) => {}} // Form submitted (e.responseId available)
onClose={() => {}} // Form closed
onQuestionChanged={(e) => {}} // Question changed (e.ref available)
onHeightChanged={(e) => {}} // Form height changed (e.height available)
onError={(error) => {}} // Error occurred
// Display options
hideHeaders={false} // Hide progress bar header
hideFooter={false} // Hide Typeform branding footer
opacity={100} // Background opacity (0-100)
disableAutoFocus={false} // Prevent auto-focus on load
enableSandbox={false} // Enable sandbox mode for testing
// Hidden fields (prefill data)
hidden={{
email: 'user@example.com',
user_id: '12345',
}}
// Tracking
source="my-app" // Source tracking parameter
medium="mobile" // Medium tracking parameter
tracking={{
gtm: 'GTM-XXXX', // Google Tag Manager
ga: 'UA-XXXX', // Google Analytics
facebookPixel: 'XXXX', // Facebook Pixel
segment: 'XXXX', // Segment
hubspot: 'XXXX', // HubSpot
}}
// Auto-resize
autoResize={false} // Auto-adjust height based on content
minHeight={300} // Minimum height when autoResize is true
maxHeight={600} // Maximum height when autoResize is true
// Loading state
showLoadingIndicator={true} // Show loading spinner
LoadingComponent={MyLoader} // Custom loading component
// Styling
style={{ flex: 1 }} // Container style
webViewProps={{}} // Additional WebView props
/>
`$3
Modal embed component for displaying forms in an overlay.
`tsx
formId="abc123" // Required: Your Typeform form ID
visible={true} // Required: Control popup visibility // Callbacks (same as TypeformWidget, plus:)
onDismiss={() => {}} // User dismissed the popup
// Modal options
size={90} // Popup size as % of screen (1-100)
animationType="slide" // 'none' | 'slide' | 'fade'
presentationStyle="fullScreen" // iOS: 'fullScreen' | 'pageSheet' | 'formSheet'
// Auto-close
autoClose={false} // Close automatically after submission
autoCloseDelay={1000} // Delay before auto-close (ms)
// Header
showCloseButton={true} // Show X button in header
headerTitle="Survey" // Optional header title
// Styling
modalStyle={{}} // Modal container style
contentStyle={{}} // Content container style
// All TypeformWidget options are also supported
hidden={{}}
hideHeaders={false}
// etc...
/>
`$3
Convenient hook for managing popup state.
`tsx
const {
// State
isVisible, // Whether popup is visible
isLoading, // Whether form is loading
isSubmitted, // Whether form was submitted
responseId, // Response ID from submission
currentQuestionRef, // Current question ref
error, // Error if any // Actions
open, // Open the popup
close, // Close the popup
toggle, // Toggle visibility
reset, // Reset state (for reuse)
// Refs & Props
popupRef, // Ref to attach to TypeformPopup
popupProps, // Props to spread on TypeformPopup
} = useTypeform({
onReady: (e) => {},
onSubmit: (e) => {},
onClose: () => {},
onQuestionChanged: (e) => {},
onError: (error) => {},
autoCloseOnSubmit: false,
});
`$3
`tsx
import {
buildTypeformUrl,
extractFormIdFromUrl,
isValidFormId,
} from 'react-native-typeform';// Build a Typeform URL with options
const url = buildTypeformUrl('abc123', {
hidden: { email: 'user@example.com' },
hideHeaders: true,
});
// -> 'https://form.typeform.com/to/abc123?email=user%40example.com&typeform-embed-hide-headers=true'
// Extract form ID from URL
const formId = extractFormIdFromUrl('https://form.typeform.com/to/abc123');
// -> 'abc123'
// Validate a form ID
const isValid = isValidFormId('abc123');
// -> true
`Advanced Examples
$3
Pass user data to prefill hidden fields in your Typeform:
`tsx
formId="abc123"
hidden={{
user_id: user.id,
email: user.email,
plan: user.subscriptionPlan,
}}
/>
`$3
`tsx
import { ActivityIndicator, View, Text } from 'react-native';function CustomLoader() {
return (
Loading survey...
);
}
formId="abc123"
LoadingComponent={CustomLoader}
/>
`$3
`tsx
function SurveyScreen() {
const handleSubmit = async (event: TypeformSubmitEvent) => {
// Response ID can be used to fetch full response via Typeform API
const { responseId } = event; // Update your backend
await api.post('/survey-completed', {
typeformResponseId: responseId,
userId: currentUser.id,
});
// Navigate away
navigation.navigate('ThankYou');
};
return (
formId="abc123"
onSubmit={handleSubmit}
hidden={{ user_id: currentUser.id }}
/>
);
}
`$3
`tsx
function SurveyFlow() {
const [step, setStep] = useState<'intro' | 'survey' | 'complete'>('intro');
const [responseId, setResponseId] = useState(null); if (step === 'intro') {
return (
Quick Survey
Help us improve your experience
);
}
if (step === 'survey') {
return (
formId="abc123"
style={{ flex: 1 }}
onSubmit={(e) => {
setResponseId(e.responseId);
setStep('complete');
}}
/>
);
}
return (
Thank You!
Your feedback helps us improve
);
}
`Troubleshooting
$3
1. Verify your form ID is correct
2. Check that the form is published and accessible
3. Ensure
react-native-webview is properly installed$3
Some Typeform callbacks require a paid plan:
-
onSubmit - Works on all plans
- onReady - Works on all plans
- onQuestionChanged - Requires Plus plan or higher$3
If the keyboard doesn't appear, ensure
keyboardDisplayRequiresUserAction={false} is set in webViewProps:`tsx
formId="abc123"
webViewProps={{
keyboardDisplayRequiresUserAction: false,
}}
/>
`$3
The popup handles the Android back button automatically via
onRequestClose.Migration from v1.x
If you're upgrading from the original
react-native-typeform:`tsx
// Before (v1.x)
import TypeformEmbed from 'react-native-typeform'; url="https://demo.typeform.com/to/njdbt5"
onSubmit={() => alert('Submitted!')}
/>
// After (v2.x)
import { TypeformWidget } from 'react-native-typeform';
formId="njdbt5"
onSubmit={(e) => alert(
Submitted! Response ID: ${e.responseId})}
/>
`Key changes:
- Use
formId instead of url
- onSubmit now receives an event object with responseId
- New components: TypeformPopup, useTypeform` hookContributions are welcome! Please read our contributing guidelines before submitting a PR.
MIT
---
Note: Typeform doesn't officially support embedding in native mobile apps via WebView. This library provides a community-maintained solution that works well for most use cases, but some features may have limitations.