Skip to main content

React / Next.js

Add ClassFlow to a React or Next.js App

Use the hosted ClassFlow loader with a data-configured widget container. The same production integration works in React, Vite, Create React App, and Next.js without adding a package dependency.

Prerequisites

  • React 18+ or Next.js 13+ (App Router or Pages Router both work).
  • A ClassFlow studio account with Widget Studio enabled.
  • Your studio slug and studio URL from Widget Studio.
  • Your app domain added to the Widget Studio allowlist.

Next.js (recommended)

Use Next.js's built-innext/scriptcomponent to load the ClassFlow script. Setstrategy="afterInteractive"so it loads after hydration and doesn't block the page.

// app/schedule/page.tsx (Next.js App Router)
import Script from 'next/script'; export default function SchedulePage() { return ( <> <div data-classflow="schedule" data-studio="your-studio-slug" data-studio-url="https://your-studio.classflow.studio" data-api-url="https://api.getclassflow.com" data-theme="light" data-days="14" /> <Script type="module" src="https://widget.classflow.studio/v1/loader.js" strategy="afterInteractive" /> </> );
}

Works with both App Router (Server and Client components) and Pages Router. The<Script>component handles deduplication - you can include it on multiple pages without loading the script twice.

Vite / Create React App

For non-Next.js React apps, add the script tag to yourindex.htmland render the div in a component. Because React re-renders can detach and re-attach DOM nodes, trigger widget initialization in auseEffect:

// For Vite / Create React App (index.html or public/index.html)
// Add the script tag once in the <head> or before </body>:
// <script type="module" src="https://widget.classflow.studio/v1/loader.js"></script>
//
// Then in your React component:
import { useEffect, useRef } from 'react'; export function ClassFlowSchedule({ studioSlug, studioUrl }: { studioSlug: string; studioUrl: string }) { const ref = useRef<HTMLDivElement>(null); useEffect(() => { // The loader observes new widget nodes; refresh is a safe SPA fallback. (window as Window & { ClassFlow?: { refresh: () => void } }).ClassFlow?.refresh(); }, []); return ( <div ref={ref} data-classflow="schedule" data-studio={studioSlug} data-studio-url={studioUrl} data-api-url="https://api.getclassflow.com" data-theme="light" data-days="14" /> );
}

Raw HTML snippet (for dangerouslySetInnerHTML)

If you need to inject the snippet as raw HTML (e.g. from a CMS), usedangerouslySetInnerHTML. Only do this if the content is your own - never inject untrusted HTML.

<div data-classflow="schedule" data-studio="your-studio-slug" data-studio-url="https://your-studio.classflow.studio" data-api-url="https://api.getclassflow.com" data-theme="light" data-days="14">
</div>
<script type="module" src="https://widget.classflow.studio/v1/loader.js"></script>

Theming

Set CSS custom properties on a wrapping element or in your global CSS. React apps don't use iframes so your app's CSS applies to the widget - keep resets minimal to avoid conflicts:

:root { --cf-primary: #0f766e; /* buttons, links */ --cf-bg: #ffffff; /* widget background */ --cf-text: #111827; /* body text */ --cf-radius: 0.75rem; /* border radius */
}

Multiple widgets on one page

Render multiple widget containers on the same page. Include the<Script>tag once—the ClassFlow runtime scans the DOM and initializes everydata-classflowelement it finds.

Common gotchas

  • SSR hydration mismatch.If you render the widget div server-side, the script runs before React hydration and can conflict. Usestrategy="afterInteractive"in Next.js or add auseEffectguard to ensure the script runs client-side only.
  • Widget unmounts on route change.In Next.js App Router, navigating away and back re-mounts the component. This is fine - the widget re-initializes on mount. Use akey prop to force clean re-mounts if needed.
  • Strict Mode double-invoke.React Strict Mode in development double-invokes effects. This may cause a brief flash. The ClassFlow widget deduplicates initialization - it is safe in Strict Mode.
  • Content Security Policy.If your app sets a CSP header, whitelistwidget.classflow.studioinscript-srcandapi.getclassflow.cominconnect-src.

Get your personalized snippet

Widget Studio generates the code with your studio slug pre-filled.

Open Widget Studio