Embed Sift in your React or Next.js app with a useEffect hook
React and Next.js apps need a tiny bit more code than CMS embeds because the script must be injected into the DOM after the component mounts. The pattern is a 20-line client component that uses useEffect to inject the Sift script and clean it up on unmount.
YOUR_FORM_ID and YOUR_PUBLIC_KEY in the snippets below.Install Sift on React / Next.js
- 1
Create a SiftWidget client component
In your project, create a new file like components/SiftWidget.tsx. In Next.js App Router, mark it with "use client" at the top.
- 2
Add the useEffect script loader
Use useEffect to create a script element on mount, set the data attributes, append it to a container ref, and clean up on unmount.
- 3
Import and use the component anywhere
Pass your form ID and API key as props: <SiftWidget formId="..." apiKey="..." />. The widget renders inline at that position.
"use client";
import { useEffect, useRef } from "react";
export function SiftWidget({
formId,
apiKey,
}: {
formId: string;
apiKey: string;
}) {
const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!containerRef.current) return;
const script = document.createElement("script");
script.src = "https://siftforms.com/widget/v1.js";
script.setAttribute("data-schema-id", formId);
script.setAttribute("data-api-key", apiKey);
script.async = true;
containerRef.current.appendChild(script);
return () => {
script.remove();
};
}, [formId, apiKey]);
return <div ref={containerRef} />;
}Alternative install methods
Next.js Script component
In Next.js you can also use the built-in Script component with strategy="afterInteractive". Note that data attributes work but the widget renders globally, not inline at the position where Script is mounted.
import Script from "next/script";
export function SiftWidget() {
return (
<Script
src="https://siftforms.com/widget/v1.js"
data-schema-id="YOUR_FORM_ID"
data-api-key="YOUR_PUBLIC_KEY"
strategy="afterInteractive"
/>
);
}Vite / Create React App
The same useEffect pattern works in Vite, Create React App, and any React-based framework. No build configuration changes required.
Tips for the best results
- Always mark the component as a client component in Next.js App Router (use client directive)
- Use environment variables for the API key: process.env.NEXT_PUBLIC_SIFT_API_KEY
- The cleanup function in useEffect prevents duplicate widgets if the component remounts
- Shadow DOM keeps the widget isolated from your Tailwind/CSS-in-JS styles
- For SSR, the widget only mounts on the client — useEffect guarantees this
Troubleshooting
Widget appears twice in development
React 18 strict mode runs effects twice in development. The cleanup function in our example handles this — make sure you are returning script.remove() from useEffect.
Hydration error: "did not match"
The widget injects DOM nodes after mount, which can confuse Next.js SSR. Make sure your SiftWidget component is marked "use client" and that you are not rendering it from a server component without that boundary.
Embed Sift on other platforms
Ready to embed Sift on React / Next.js?
Create your first form in your dashboard, then come back and paste the snippet above.