Embed in Next.js
Add GenZform forms to Next.js applications with proper client component patterns and Script optimization.
GenZform forms work with Next.js App Router and Pages Router. This guide covers client components, Script optimization, and server-side considerations.
Client Component (App Router)
Since embeds require browser APIs, create a client component:
// components/GenZformEmbed.tsx
'use client';
import Script from 'next/script';
interface GenZformEmbedProps {
formId: string;
height?: number | string;
minHeight?: number;
className?: string;
locale?: string;
}
export function GenZformEmbed({
formId,
height = 500,
minHeight,
className,
locale = 'en',
}: GenZformEmbedProps) {
return (
<>
<iframe
data-genz-form={formId}
data-embed-style="standard"
data-min-height={minHeight}
src={`https://genzform.com/f/${formId}?locale=${locale}`}
style={{
width: '100%',
height: typeof height === 'number' ? `${height}px` : height,
border: 'none',
}}
className={className}
title="GenZform"
/>
<Script
src="https://genzform.com/embed-v1.js"
strategy="lazyOnload"
/>
</>
);
}Use it in any page or layout:
// app/contact/page.tsx
import { GenZformEmbed } from '@/components/GenZformEmbed';
export default function ContactPage() {
return (
<main>
<h1>Contact Us</h1>
<GenZformEmbed formId="your-form-id" height={600} />
</main>
);
}Popup Component
// components/GenZformPopup.tsx
'use client';
import Script from 'next/script';
import { useEffect, useState } from 'react';
interface GenZformPopupProps {
formId: string;
children: React.ReactNode;
width?: number;
darkOverlay?: boolean;
openOnLoad?: boolean;
openDelay?: number;
className?: string;
}
export function GenZformPopup({
formId,
children,
width = 450,
darkOverlay = true,
openOnLoad = false,
openDelay = 0,
className,
}: GenZformPopupProps) {
const [scriptLoaded, setScriptLoaded] = useState(false);
useEffect(() => {
if (scriptLoaded) {
// Register popup buttons after script loads
(window as any).GenZEmbed?.registerPopups?.();
}
}, [scriptLoaded]);
return (
<>
<button
data-genz-form={formId}
data-embed-style="popup"
data-width={width}
data-dark-overlay={darkOverlay}
data-popup-open-on-load={openOnLoad}
data-popup-delay={openDelay}
className={className}
>
{children}
</button>
<Script
src="https://genzform.com/embed-v1.js"
strategy="lazyOnload"
onLoad={() => setScriptLoaded(true)}
/>
</>
);
}Usage:
<GenZformPopup
formId="your-form-id"
darkOverlay={true}
className="bg-green-600 text-white px-4 py-2 rounded"
>
Get Started
</GenZformPopup>Full Page Form
For dedicated form pages that fill the viewport:
// components/GenZformFullPage.tsx
'use client';
import Script from 'next/script';
export function GenZformFullPage({
formId,
locale = 'en',
}: {
formId: string;
locale?: string;
}) {
return (
<div className="fixed inset-0">
<iframe
data-genz-form={formId}
data-embed-style="full_page"
src={`https://genzform.com/f/${formId}?locale=${locale}`}
className="w-full h-full border-none"
title="GenZform"
/>
<Script src="https://genzform.com/embed-v1.js" strategy="lazyOnload" />
</div>
);
}Pages Router
For the Pages Router, use next/script the same way:
// pages/contact.tsx
import Script from 'next/script';
export default function ContactPage() {
return (
<>
<main>
<h1>Contact Us</h1>
<iframe
data-genz-form="your-form-id"
data-embed-style="standard"
src="https://genzform.com/f/your-form-id"
style={{ width: '100%', height: '500px', border: 'none' }}
title="GenZform"
/>
</main>
<Script
src="https://genzform.com/embed-v1.js"
strategy="lazyOnload"
/>
</>
);
}Script Loading Strategies
Next.js Script component supports different loading strategies:
| Strategy | Use When |
|---|---|
lazyOnload | Default. Loads after page is interactive. Best for most forms. |
afterInteractive | Loads immediately after page becomes interactive. Use for above-the-fold forms. |
beforeInteractive | Loads before page hydration. Rarely needed for forms. |
// For forms immediately visible on page load
<Script
src="https://genzform.com/embed-v1.js"
strategy="afterInteractive"
/>Dynamic Form Loading
Load forms dynamically based on route or user action:
'use client';
import dynamic from 'next/dynamic';
const GenZformEmbed = dynamic(
() => import('@/components/GenZformEmbed').then(mod => mod.GenZformEmbed),
{ ssr: false }
);
export default function DynamicFormPage({ formId }: { formId: string }) {
return <GenZformEmbed formId={formId} />;
}With Internationalization
Pass the locale from your i18n setup:
// app/[locale]/contact/page.tsx
import { GenZformEmbed } from '@/components/GenZformEmbed';
export default function ContactPage({
params: { locale },
}: {
params: { locale: string };
}) {
return (
<GenZformEmbed
formId="your-form-id"
locale={locale}
/>
);
}Frequently Asked Questions
Why do I need 'use client'?
The embed script and iframe interactions require browser APIs. Next.js App Router components are server components by default, so you need 'use client' to access browser features.
Can I use the form in a Server Component?
Not directly. Wrap the form in a client component and import that into your server component. The form itself needs browser APIs to function.
Does the Script component prevent duplicate loading?
Yes. Next.js Script component automatically deduplicates scripts with the same src, even if you include the component multiple times.
How do I handle form submission callbacks?
See the Custom JavaScript guide for listening to form events and handling submissions.