### Install @formsort/web-embed-api with npm
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Install the web embed API package using npm.
```shell
npm install @formsort/web-embed-api --save
```
--------------------------------
### Install @formsort/web-embed-api with Yarn
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Install the web embed API package using Yarn.
```shell
yarn add @formsort/web-embed-api
```
--------------------------------
### Install @formsort/constants
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/ReactNativeEmbed.md
Install the constants package for Formsort integration. Use npm or yarn.
```bash
npm install @formsort/constants # or yarn add @formsort/constants
```
--------------------------------
### Install Project Dependencies
Source: https://github.com/formsort/oss/blob/master/CONTRIBUTING.md
Run this command to install all dependencies for the sub-packages and create symlinks. Volta is recommended for managing Node.js versions.
```shell
yarn install --frozen-lockfile
```
--------------------------------
### Install @formsort/tsconfig
Source: https://github.com/formsort/oss/blob/master/packages/tsconfig/README.md
Install the shared TypeScript configuration as a development dependency using Yarn.
```shell
yarn add --dev @formsort/tsconfig
```
--------------------------------
### Install Custom Question API
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Install the @formsort/custom-question-api package using either yarn or npm.
```shell
yarn add @formsort/custom-question-api
```
```shell
npm install --save @formsort/custom-question-api
```
--------------------------------
### Install webview_flutter
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/FlutterEmbed.md
Add the webview_flutter package to your Flutter project dependencies.
```bash
flutter pub add webview_flutter
```
--------------------------------
### Install Dependencies for ESLint Config
Source: https://github.com/formsort/oss/blob/master/packages/eslint-config/README.md
Install the @formsort/eslint-config package and its peer dependencies as development dependencies.
```bash
yarn add --dev @formsort/eslint-config eslint-plugin-prefer-arrow eslint-plugin-jsdoc@24 eslint-plugin-prettier eslint-config-prettier eslint-plugin-import prettier eslint-plugin-react @typescript-eslint/eslint-plugin
```
--------------------------------
### loadFlow
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Starts loading a Formsort variant or a flow into the initialized embed. If a variant label is not provided, a variant will be chosen based on weights or randomly.
```APIDOC
## loadFlow(clientLabel: string, flowLabel: string, variantLabel?: string, queryParams?: Array<[string, string]>) => void
Starts loading a Formsort variant, or a flow.
Note that `variantLabel` is optional. If it is not provided, a variant will be chosen from the flow:
- Based on weights, if weights are assigned
- At random, if no weights are assigned
```
--------------------------------
### Embedding a flow in React Native via `react-native-webview`
Source: https://context7.com/formsort/oss/llms.txt
This example demonstrates how to embed a Formsort flow within a React Native application using the `react-native-webview` component. It includes a JavaScript snippet to bridge `window.postMessage` to `ReactNativeWebView.postMessage` for communication between the webview and the native app, and shows how to handle flow events.
```APIDOC
## Embedding a flow in React Native via `react-native-webview`
Formsort flows can be embedded in React Native using `react-native-webview`. A JavaScript snippet must be injected to bridge `window.postMessage` to `ReactNativeWebView.postMessage`, and the `@formsort/constants` package is used to identify the message type.
```tsx
import React from 'react';
import { StyleSheet, SafeAreaView } from 'react-native';
import { WebView } from 'react-native-webview';
import { WebEmbedMessage } from '@formsort/constants';
// Bridge postMessage to React Native's native message channel
const injectPostMessage = `
window.postMessage = (data) => window.ReactNativeWebView.postMessage(JSON.stringify(data));
`;
const flowUrl = 'https://acme.formsort.app/flow/onboarding/variant/main';
export default function App() {
const onMessage = (event: { nativeEvent: { data: string } }) => {
const eventData = JSON.parse(event.nativeEvent.data);
if (eventData.type === WebEmbedMessage.EMBED_EVENT_MSG) {
switch (eventData.eventType) {
case 'FlowLoaded':
console.log('Flow loaded');
break;
case 'FlowFinalized':
console.log('Flow finalized, answers:', eventData.answers);
break;
case 'StepCompleted':
console.log('Step completed:', eventData.stepId);
break;
default:
break;
}
}
};
return (
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#fff', justifyContent: 'center' },
webView: { flex: 1 },
});
```
```
--------------------------------
### React Native Embed Example
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/ReactNativeEmbed.md
Integrate a Formsort flow into a React Native app using react-native-webview. Handles messages from the flow and allows navigation.
```tsx
import { StyleSheet, SafeAreaView } from 'react-native';
import { WebView } from 'react-native-webview';
import { WebEmbedMessage } from '@formsort/constants'
/**
* Injects a postMessage function into the webview to allow communication
*/
const injectPostMessage = `
window.postMessage = (data) => window.ReactNativeWebView.postMessage(JSON.stringify(data));
`;
// replace with your flow url
const flowUrl = 'https://uwymnmujkn.formsort.app/flow/booking-flow-test/variant/B';
export default function App() {
const onMessage = (event) => {
const eventData = JSON.parse(event.nativeEvent.data);
// get messages from the flow
if (eventData.type === WebEmbedMessage.EMBED_EVENT_MSG) {
switch(event.data.eventType) {
case 'FlowCompleted':
console.log('Flow completed');
// do something when the flow is completed
break;
case 'FlowStarted':
console.log('Flow started');
// do something when the flow is started
break;
default:
break;
}
}
}
return (
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center',
},
webView: {
flex: 1,
}
});
```
--------------------------------
### Get Custom Question Answer Value
Source: https://context7.com/formsort/oss/llms.txt
Use `getAnswerValue()` to retrieve the current answer for a custom question. Call this on mount to restore previously entered values, for example, when the user navigates back.
```ts
import { getAnswerValue } from '@formsort/custom-question-api';
// In your custom question's initialization code:
async function initialize() {
const currentValue = await getAnswerValue();
if (currentValue !== undefined) {
// Restore the previously entered value
(document.getElementById('my-input') as HTMLInputElement).value = currentValue;
}
}
initialize();
```
--------------------------------
### Set Question Size with React
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Example of using `setQuestionSize` within a React component to dynamically set the question's dimensions based on its rendered element.
```typescript
import React, { useEffect, useRef } from 'react';
import { setQuestionSize } from '@formsort/custom-question-api';
const MyCustomComponent = () => {
const containerElRef = useRef();
useEffect(() => {
const containerEl = containerElRef.current;
if (!containerEl) {
return;
}
setQuestionSize(containerEl.offsetWidth, containerEl.offsetHeight);
}, []);
return (
My custom component
);
};
```
--------------------------------
### Get All Answer Values
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Retrieves all answers provided by the recipient so far. The keys are the variable names defined in Formsort.
```typescript
getAllAnswerValues() => Promise<{ [key: string]: any }>
```
```typescript
getAllAnswerValues() => Promise<{ [key: string]: any}>
```
--------------------------------
### Basic EmbedFlow Usage in React
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/README.md
Demonstrates how to use the EmbedFlow component to embed a Formsort flow in a React application. Ensure you have installed the package.
```tsx
import React from 'react';
import EmbedFlow from '@formsort/react-embed';
const EmbedFlowExample: React.FunctionComponent = () => (
);
```
--------------------------------
### Get All Answer Values with getAllAnswerValues()
Source: https://context7.com/formsort/oss/llms.txt
Retrieves a promise that resolves to a key/value map of all answers collected in the flow. Keys correspond to variable names configured in Formsort studio.
```typescript
import { getAllAnswerValues } from '@formsort/custom-question-api';
async function prefillFromContext() {
const allAnswers = await getAllAnswerValues();
// allAnswers = { first_name: 'Jane', email: 'jane@example.com', age: 28 }
if (allAnswers['first_name']) {
document.getElementById('greeting')!.textContent =
`Hello, ${allAnswers['first_name']}!`;
}
}
prefillFromContext();
```
--------------------------------
### Embedding a flow in Flutter via `webview_flutter`
Source: https://context7.com/formsort/oss/llms.txt
This example shows how to embed a Formsort flow in a Flutter application using the `webview_flutter` package. It includes JavaScript injection to establish a communication channel between the webview and the Flutter app, enabling the app to receive flow events.
```APIDOC
## Embedding a flow in Flutter via `webview_flutter`
Formsort flows can be embedded in Flutter apps using `webview_flutter`. JavaScript injection bridges `window.postMessage` to a Flutter channel so the app can receive flow events.
```dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:convert';
// Special prefix that identifies Formsort embed event messages
const String EMBED_EVENT_MSG = 'ƒ_wee';
class FormsortFlowPage extends StatefulWidget {
const FormsortFlowPage({super.key});
@override
State createState() => _FormsortFlowPageState();
}
class _FormsortFlowPageState extends State {
late final WebViewController controller;
final flowUrl =
'https://acme.formsort.app/flow/onboarding/variant/main';
// Patch window.postMessage to route through the Flutter channel
final String setUpJSWebViewMessaging = """;
window.FlutterWebView = true;
window.postMessage = function(data) {
FlutterChannel.postMessage(JSON.stringify(data));
};
""";
@override
void initState() {
super.initState();
controller = WebViewController()
..loadRequest(Uri.parse(flowUrl))
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel('FlutterChannel', onMessageReceived: (event) {
final eventData = jsonDecode(event.message);
if (eventData['type'] == EMBED_EVENT_MSG) {
switch (eventData['eventType']) {
case 'FlowLoaded':
print('Flow loaded');
break;
case 'FlowFinalized':
print('Flow finalized: ${eventData['answers']}');
break;
default:
break;
}
}
})
..setNavigationDelegate(NavigationDelegate(
onPageFinished: (_) => controller.runJavaScript(setUpJSWebViewMessaging),
));
}
@override
Widget build(BuildContext context) {
return Scaffold(body: WebViewWidget(controller: controller));
}
}
```
```
--------------------------------
### Get Answer Value
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Retrieves the current value of the answer for this question. Optionally specify the type with a generic parameter. The value may be undefined.
```typescript
getAnswerValue() => Promise
```
```typescript
getAnswerValue();
```
--------------------------------
### Get Semantic Answer Value
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Retrieves a specific semantic answer value (e.g., responder_email). This allows looking up answers by their meaning, not just variable name.
```typescript
getSemanticAnswerValue(semanticType: AnswerSemanticType) => Promise
```
--------------------------------
### Project Configuration (.env file)
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/examples/formsort-embed-example-lit/README.md
Configure your Formsort integration by setting environment variables in the .env file. This includes your client ID, flow ID, and variant name. Optionally, specify a custom Formsort origin.
```env
CLIENT = my-client
FLOW = first-flow
VARIANT = main
```
--------------------------------
### getResponderUuid
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Gets the current responder's UUID, which can be used for external lookups.
```APIDOC
## getResponderUuid
### Description
Retrieves the unique identifier for the current responder.
### Method Signature
```typescript
getResponderUuid(): Promise
```
### Returns
- `Promise`: A promise that resolves to the responder's UUID.
```
--------------------------------
### Configure ESLint with @formsort/eslint-config
Source: https://github.com/formsort/oss/blob/master/packages/eslint-config/README.md
Set up your project's .eslintrc.js file to extend the shared ESLint configuration.
```javascript
module.exports = {
extends: "@formsort/eslint-config"
};
```
--------------------------------
### Get Responder UUID
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Retrieves the unique identifier for the current responder. Useful for external lookups.
```typescript
getResponderUuid() => Promise
```
```typescript
getResponderUuid() => Promise
```
--------------------------------
### Restart Flow with Query Parameter
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/ReactNativeEmbed.md
Append '?discardAnswers=true' to the flow URL to restart the flow every time the user returns.
```tsx
const flowUrl = 'https://uwymnmujkn.formsort.app/flow/booking-flow-test/variant/B?discardAnswers=true';
```
--------------------------------
### Development Configuration
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Configure the Formsort Web Embed to point to a different flow server during development by setting the 'origin' option in the configuration object.
```APIDOC
## Development Configuration
By default, the web embed accesses the production formsort servers. If you would like to point to another flow server, set `origin` in the config to the correct base URL, for example:
```ts
FormsortWebEmbed(document.body, { origin: 'http://localhost:4040' });
```
```
--------------------------------
### Add ESLint Scripts to package.json
Source: https://github.com/formsort/oss/blob/master/packages/eslint-config/README.md
Include 'format' and 'lint' scripts in your package.json for convenient code formatting and linting.
```json
{
"scripts": {
"format": "eslint --ext .ts,.tsx src --fix",
"lint": "eslint --ext .ts,.tsx src"
}
}
```
--------------------------------
### Initialize FormsortWebEmbed
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Initialize the Formsort embed instance by providing the root HTML element where the iframe will be appended.
```typescript
const embed = FormsortWebEmbed(document.body);
```
--------------------------------
### loadFlow(clientLabel, flowLabel, variantLabel?, queryParams?)
Source: https://context7.com/formsort/oss/llms.txt
Loads a specific Formsort flow and optional variant into the initialized embed. Query parameters can be used for pre-population or specific revision loading.
```APIDOC
## loadFlow(clientLabel, flowLabel, variantLabel?, queryParams?)
### Description
Starts loading a specific Formsort flow (and optional variant) inside the previously initialized iframe. If `variantLabel` is omitted, a variant is chosen by weight or at random. Query params can be used to pre-populate answers or load a specific variant revision.
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Parameters
- **clientLabel** (string) - Required - The client label for the Formsort flow.
- **flowLabel** (string) - Required - The label of the flow to load.
- **variantLabel** (string) - Optional - The label of the specific variant to load. If omitted, a variant is chosen by weight or at random.
- **queryParams** (Array<[string, string]>) - Optional - An array of key-value pairs for query parameters. Used to pre-populate answers or load specific revisions (e.g., `['name', 'Jane Doe']`, `['variantRevisionUuid', 'b3f1a2c4-...']`).
### Request Example
```javascript
// Load a specific variant
embed.loadFlow('acme', 'onboarding', 'main');
// Load with pre-populated answers and environment override
embed.loadFlow('acme', 'onboarding', 'main', [
['name', 'Jane Doe'],
['email', 'jane@example.com'],
['formsortEnv', 'staging'],
// Load a pinned revision (avoid showing latest):
// ['variantRevisionUuid', 'b3f1a2c4-...'],
]);
```
### Response
#### Success Response (200)
None (This method performs an action and does not return data directly).
```
--------------------------------
### Initialize Formsort Web Embed
Source: https://context7.com/formsort/oss/llms.txt
Creates and appends a Formsort iframe to a specified DOM element. Configure iframe behavior, styling, and authentication. Use `useHistoryAPI` for SPA compatibility and `autoHeight` for dynamic resizing.
```typescript
import FormsortWebEmbed from '@formsort/web-embed-api';
// Basic initialization with configuration
const embed = FormsortWebEmbed(document.getElementById('form-container')!, {
useHistoryAPI: true, // Use window.history.pushState for redirects (SPA-friendly)
autoHeight: true, // Auto-resize iframe to match flow content height
iframeTitle: 'Onboarding', // Accessibility title for the iframe
iframeAllow: 'camera;', // Permissions policy (e.g. for camera access)
style: {
width: '100%',
height: '600px',
},
authentication: {
idToken: 'eyJhbGciOiJSUzI1NiIsInR5cCI6...', // JWT for authenticated flows
},
// origin: 'https://acme-flow.com', // Uncomment to use a custom domain
});
```
--------------------------------
### Get Responder UUID with getResponderUuid()
Source: https://context7.com/formsort/oss/llms.txt
Returns a promise for the unique ID of the current form responder. This ID is useful for performing external lookups against your own databases.
```typescript
import { getResponderUuid } from '@formsort/custom-question-api';
async function loadExternalData() {
const uuid = await getResponderUuid();
// uuid = 'e4923baa-dc2d-4555-813c-a166952292fa'
const response = await fetch(`/api/users/${uuid}/preferences`);
const prefs = await response.json();
applyPreferences(prefs);
}
loadExternalData();
```
--------------------------------
### Configure Package for Public NPM Registry
Source: https://github.com/formsort/oss/blob/master/CONTRIBUTING.md
Add this configuration to your package.json to make the package public on the npm registry. This is required when adding new public packages.
```json
{
"publishConfig": {
"access": "public"
}
}
```
--------------------------------
### Import Helper Functions
Source: https://github.com/formsort/oss/blob/master/packages/custom-question-api/README.md
Import necessary helper functions from the @formsort/custom-question-api library into your custom question implementation.
```javascript
import {
getAnswerValue,
setAnswerValue,
clearAnswerValue,
getSemanticAnswerValue,
getAllAnswerValues,
getResponderUuid,
setQuestionSize,
setDisableBackNavigation,
setAutoHeight,
} from '@formsort/custom-question-api';
```
--------------------------------
### Get Semantic Answer Value with getSemanticAnswerValue()
Source: https://context7.com/formsort/oss/llms.txt
Fetches an answer by its semantic type, such as 'responder_email'. This is more robust than using variable names as Formsort maps semantic types to variables.
```typescript
import { getSemanticAnswerValue } from '@formsort/custom-question-api';
async function loadUserContext() {
const email = await getSemanticAnswerValue('responder_email');
const firstName = await getSemanticAnswerValue('responder_first_name');
const phone = await getSemanticAnswerValue('responder_phone');
// Supported semantic types:
// 'responder_email' | 'responder_first_name' | 'responder_last_name'
// 'responder_phone' | 'responder_dob' | 'responder_mailing_address'
// 'responder_user_id' | 'responder_marketing_consent' | 'responder_other_pii'
console.log(`Welcome ${firstName} (${email})`);
}
loadUserContext();
```
--------------------------------
### Customizing Flow Behavior with URL Parameter
Source: https://github.com/formsort/oss/blob/master/packages/react-embed/FlutterEmbed.md
Append '?discardAnswers=true' to the flow URL to ensure the flow restarts every time a user returns to it.
```dart
final flowUrl = 'https://formsorttemplatesdemo.formsort.app/flow/template-patient-onboarding-demo/variant/patient-onboarding-customization?discardAnswers=true';
```
--------------------------------
### FormsortWebEmbed Initialization
Source: https://github.com/formsort/oss/blob/master/packages/web-embed-api/README.md
Initializes a Formsort iframe as a child of the provided root element. Configuration options can be passed to customize the embed behavior and appearance.
```APIDOC
## FormsortWebEmbed(rootEl: HTMLElement, config?: IFormsortWebEmbedConfig)
Initializes a Formsort iframe as a child of the `rootEl` provided.
```ts
const embed = FormsortWebEmbed(document.body);
```
The optional `config` object has the following interface:
```ts
interface IFormsortWebEmbedConfig {
useHistoryAPI?: boolean; // Default: false
/**
* iframe title attribute for accessibility
*/
iframeTitle?: string;
/**
* iframe allow attribute for permissions
* e.g. "camera;"
*/
iframeAllow?: string;
autoHeight?: boolean; // Default: false
style?: {
width?: CSSStyleDeclaration['width'];
height?: CSSStyleDeclaration['height'];
};
authentication?: {
idToken?: string; // ID Token for authenticated flows
};
origin?: string; // optional -- use if you want to load your flow in a custom damain. e.g. https://acme-flow.com
}
```
#### `config` properties
- `useHistoryAPI`: When redirecting, should we use the HTML5 History API (namely, `window.pushState`), or just change the URL in its entirety?
Helpful if you have a single-page app and want to change the container's URL without reloading the entire page. Note that you'll have to listen to the `popstate` event on the embedding `window` to detect this navigation.
- `autoHeight`: Should the embedding `