Skip to content
Last updated

Embedding forms in React

Iframe embed

The plain iframe embed needs no special handling; render it like any other element. JSX uses camelCase for the boolean attributes and an object for style:

function CakeOrderForm() {
  return (
    <iframe
      title="Cake Order Form"
      src="https://eu.forms.app/form/69d4bd130b443bda40c8f65a"
      allowTransparency
      allowFullScreen
      allow="geolocation; microphone; camera"
      style={{ width: '100vw', minWidth: '100%', height: 600, border: 'none' }}
    />
  );
}

Script embed

The script embed's onload inline attribute isn't idiomatic in React. Instead, load embed.js in a useEffect, and call new window.formsapp(...) once it's ready. A small hook keeps the script from being injected more than once if you render several widgets:

import { useEffect, useState } from 'react';

function useFormsAppScript() {
  const [ready, setReady] = useState(() => typeof window !== 'undefined' && !!window.formsapp);

  useEffect(() => {
    if (window.formsapp) {
      setReady(true);
      return;
    }
    const existing = document.querySelector('script[src="/api/v1/web-embed/proxy?proxyUrl=https%3A%2F%2Fcdn.formsapp.io%2Fembed.js&mode=full"]');
    if (existing) {
      existing.addEventListener('load', () => setReady(true));
      return;
    }
    const script = document.createElement('script');
    script.src = 'https://cdn.formsapp.io/embed.js';
    script.async = true;
    script.defer = true;
    script.onload = () => setReady(true);
    document.body.appendChild(script);
  }, []);

  return ready;
}

Use it in a component that renders a pop-up trigger:

function CakeOrderPopup() {
  const ready = useFormsAppScript();

  useEffect(() => {
    if (!ready) return;
    new window.formsapp(
      '69d4bd130b443bda40c8f65a',
      'popup',
      {
        overlay: 'rgba(45,45,45,0.5)',
        button: { color: '#ff9e24', text: 'Click here!' },
        width: '800px',
        height: '600px',
        openingAnimation: { entrance: 'animate__fadeIn', exit: 'animate__fadeOut' },
      },
      'https://eu.forms.app',
    );
  }, [ready]);

  return <button formsappid="69d4bd130b443bda40c8f65a" />;
}
Attribute casing

HTML attribute names are case-insensitive, so formsappid and formsappId resolve to the same DOM attribute. React only recognizes the lowercase spelling for unknown attributes and otherwise logs a harmless development warning, so write it as formsappid in JSX.

Passing data from a URL query parameter

Combine the hook above with URLSearchParams and the answers option from Passing data to your form:

function ReferralForm() {
  const ready = useFormsAppScript();
  const [ref] = useState(() => new URLSearchParams(window.location.search).get('ref') ?? '');

  useEffect(() => {
    if (!ready) return;
    new window.formsapp(
      '69d4bd130b443bda40c8f65a',
      'standard',
      {
        width: '100vw',
        height: 'formHeight',
        answers: { '63ebad419442ad0448b9e9b6': ref },
      },
      'https://eu.forms.app',
    );
  }, [ready, ref]);

  return <div data-formsapp-src="https://eu.forms.app/form/69d4bd130b443bda40c8f65a" />;
}

What's next

  • Next.js for the App Router equivalent, including next/script.
  • Embed options for every layout's settings.