React
Compatibility
Section titled “Compatibility”The SDK is a browser library. It relies on window, document, navigator, and localStorage, and renders directly into a DOM element with the id you provide. It works in any React app that runs in the browser (Vite, CRA, Next.js, Remix, etc.).
For SSR frameworks (Next.js App Router, Remix), instantiate the SDK only on the client — inside useEffect, or from a 'use client' component.
Installation
Section titled “Installation”npm install @magicfeedback/nativeImport the stylesheet once, at the entry point of your app:
// e.g. main.tsx, _app.tsx, layout.tsximport "@magicfeedback/native/dist/styles/magicfeedback-default.css";Basic component
Section titled “Basic component”The minimal pattern: a container <div> with a stable id, and a useEffect that calls init and generate once.
import { useEffect } from "react";import magicfeedback from "@magicfeedback/native";
export function FeedbackSurvey() { useEffect(() => { magicfeedback.init({ env: "prod" });
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY"); form.generate("survey-root", { addButton: true, addSuccessScreen: true, }); }, []);
return <div id="survey-root" />;}Reusable hook
Section titled “Reusable hook”If you mount the survey in more than one place, wrap the setup in a hook:
import { useEffect } from "react";import magicfeedback from "@magicfeedback/native";import type { generateFormOptions } from "@magicfeedback/native/dist/types/src/models/types";
export function useMagicFeedbackForm( containerId: string, appId: string, publicKey: string, options?: generateFormOptions,) { useEffect(() => { magicfeedback.init({ env: "prod" }); const form = magicfeedback.form(appId, publicKey); form.generate(containerId, options); }, [containerId, appId, publicKey]);}function FeedbackPage() { useMagicFeedbackForm("survey-root", "APP_ID", "PUBLIC_KEY", { addButton: true, questionFormat: "slim", });
return <div id="survey-root" />;}Next.js (App Router)
Section titled “Next.js (App Router)”"use client";
import { useEffect } from "react";import magicfeedback from "@magicfeedback/native";import "@magicfeedback/native/dist/styles/magicfeedback-default.css";
export function FeedbackSurvey() { useEffect(() => { magicfeedback.init({ env: "prod" }); const form = magicfeedback.form( process.env.NEXT_PUBLIC_MF_APP_ID!, process.env.NEXT_PUBLIC_MF_PUBLIC_KEY!, ); form.generate("survey-root", { addButton: true }); }, []);
return <div id="survey-root" />;}Rendering in a modal
Section titled “Rendering in a modal”The SDK doesn’t draw the modal — your design system does. Mount the survey only when the modal opens.
import { useEffect } from "react";import magicfeedback from "@magicfeedback/native";
export function FeedbackModal({ open, onClose,}: { open: boolean; onClose: () => void;}) { useEffect(() => { if (!open) return;
const form = magicfeedback.form("APP_ID", "PUBLIC_KEY"); form.generate("survey-root", { addButton: true, addSuccessScreen: true, questionFormat: "slim", afterSubmitEvent: ({ completed }) => { if (completed) onClose(); }, }); }, [open]);
if (!open) return null;
return ( <Dialog onClose={onClose}> <div id="survey-root" /> </Dialog> );}See Rendering surfaces for more patterns (drawer, bottom sheet, dedicated page).
Lifecycle events
Section titled “Lifecycle events”useEffect(() => { const form = magicfeedback.form("APP_ID", "PUBLIC_KEY"); form.generate("survey-root", { onLoadedEvent: () => analytics.track("survey_loaded"), afterSubmitEvent: ({ completed }) => { if (completed) analytics.track("survey_completed"); }, });}, []);See Lifecycle events for the full surface.