### 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 `