Embedding in React & Next.js
Add FavForm to your React or Next.js application with our SDK or iframe.
Quick start (Script tag)
The simplest approach — load the SDK and use the web components:
// app/contact/page.tsx (Next.js App Router)
import Script from 'next/script'
export default function ContactPage() {
return (
<div>
<h1>Contact Us</h1>
{/* FavForm embed */}
<fav-form data-embedId="your-form-id" />
{/* Load SDK */}
<Script
src="https://favform.com/embed/v1.js"
strategy="lazyOnload"
/>
</div>
)
}TypeScript declarations
Add type declarations for the custom elements:
// types/favform.d.ts
declare namespace JSX {
interface IntrinsicElements {
'fav-form': React.DetailedHTMLProps<
React.HTMLAttributes<HTMLElement> & {
'data-embedId'?: string
},
HTMLElement
>
}
}Reusable component
Create a reusable FavForm component:
// components/FavForm.tsx
'use client'
import { useEffect } from 'react'
interface FavFormProps {
embedId: string
className?: string
}
export function FavForm({ embedId, className }: FavFormProps) {
useEffect(() => {
// Load SDK if not already loaded
if (typeof window !== 'undefined' && !document.querySelector('script[src*="favform.com"]')) {
const script = document.createElement('script')
script.src = 'https://favform.com/embed/v1.js'
script.async = true
document.body.appendChild(script)
}
}, [])
return (
<div className={className}>
<fav-form data-embedId={embedId} />
</div>
)
}
// Usage:
// <FavForm embedId="your-form-id" />Using iframe
For simpler integration without custom elements:
// components/FavFormIframe.tsx
interface FavFormIframeProps {
formId: string
height?: number
className?: string
}
export function FavFormIframe({
formId,
height = 600,
className
}: FavFormIframeProps) {
return (
<iframe
src={`https://favform.com/f/${formId}`}
width="100%"
height={height}
frameBorder="0"
className={className}
style={{ border: 'none' }}
title="FavForm"
/>
)
}Next.js App Router
For Next.js 13+ with the App Router:
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{children}
{/* Load FavForm SDK globally */}
<Script
src="https://favform.com/embed/v1.js"
strategy="lazyOnload"
/>
</body>
</html>
)
}Site-wide widget
Add a feedback widget to your entire app:
// app/layout.tsx
import Script from 'next/script'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
{children}
{/* Feedback widget */}
<fav-form data-embedId="your-widget-id" />
<Script
src="https://favform.com/embed/v1.js"
strategy="lazyOnload"
/>
</body>
</html>
)
}Dynamic form IDs
Load different forms based on route or state:
// app/forms/[slug]/page.tsx
import { FavForm } from '@/components/FavForm'
const embedIds: Record<string, string> = {
contact: 'form-id-1',
feedback: 'form-id-2',
support: 'form-id-3',
}
export default function FormPage({
params
}: {
params: { slug: string }
}) {
const embedId = embedIds[params.slug]
if (!embedId) {
return <div>Form not found</div>
}
return <FavForm embedId={embedId} />
}Polls and widgets
// Poll component
<fav-form data-embedId="your-poll-id" />
// Widget component
<fav-form data-embedId="your-widget-id" />Troubleshooting
Hydration mismatch
Custom elements may cause hydration warnings. Use 'use client' and load the SDK in useEffect.
TypeScript errors
Add the type declarations shown above to your project's types folder.
Form not rendering in SSR
Custom elements only work client-side. The form will render after hydration. Use the iframe method for SSR-friendly embedding.
Script loading multiple times
Check for the script before adding it, or load it once in your root layout.