Examples use the App Router.
Client Components only
The script embed reads and writes to window and the DOM, so any component that calls formsapp() needs the 'use client' directive. The plain iframe embed has no such requirement and can render from a Server Component.
export 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' }}
/>
);
}Use next/script instead of manually injecting a <script> tag; it handles load scheduling and de-duplication for you.
'use client';
import Script from 'next/script';
declare global {
interface Window {
formsapp: new (
formId: string,
layout: string,
options: Record<string, unknown>,
domain: string,
) => void;
}
}
export function CakeOrderPopup() {
return (
<>
<button formsappid="69d4bd130b443bda40c8f65a" />
<Script
src="https://cdn.formsapp.io/embed.js"
strategy="lazyOnload"
onLoad={() => {
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',
);
}}
/>
</>
);
}Use useSearchParams from next/navigation, together with the answers option from Passing data to your form:
'use client';
import Script from 'next/script';
import { useSearchParams } from 'next/navigation';
export function ReferralForm() {
const searchParams = useSearchParams();
const ref = searchParams.get('ref') ?? '';
return (
<>
<div data-formsapp-src="https://eu.forms.app/form/69d4bd130b443bda40c8f65a" />
<Script
src="https://cdn.formsapp.io/embed.js"
strategy="lazyOnload"
onLoad={() => {
new window.formsapp(
'69d4bd130b443bda40c8f65a',
'standard',
{ width: '100vw', height: 'formHeight', answers: { '63ebad419442ad0448b9e9b6': ref } },
'https://eu.forms.app',
);
}}
/>
</>
);
}Wrap in Suspense
useSearchParams opts a route into client-side rendering for the part of the tree that calls it. Wrap the component in a <Suspense> boundary, per the Next.js guidance, to avoid deopting the whole page.
- React for the Pages Router / plain React equivalent.
- Embed options for every layout's settings.