React and Next.js

Use the shared installation checks. Keep the actual generated configuration; adapt the loader to the app's existing script lifecycle rather than pasting script tags into a component's returned markup.

Choose one lifecycle owner

For a persistent floating widget, mount one client-side owner in the shared app shell/layout. Load the SDK once and initialize the widget once for that owner. Separate SDK available from widget mounted: window.voiceflow can still exist after destroy() or after an embedded target disappeared.

For page-owned embedded chat, initialize only after its target mounts and call destroy() when that owner really unmounts. Dispose owned listeners. Prevent a late script-load callback from initializing against an unmounted target. If a new owner appears while loading, it must still be able to initialize. Do not destroy a shared widget from a child page's cleanup.

React development Strict Mode deliberately repeats setup/cleanup. Exercise that cycle; do not turn Strict Mode off or use a permanent guard that suppresses the second mount after the first cleanup destroyed the widget. Avoid depending on a freshly allocated configuration object that retriggers setup every render.

Next.js

Use a Client Component for browser API access and script callbacks. A suitable shared layout can include that component without becoming a client-only app. next/script provides loading strategies; afterInteractive suits a readily available launcher, while lazyOnload deliberately delays it. Respect the existing site's choice rather than adding a second loader.

An inline Script needs a stable id. With an external script, remember that onReady runs again on component mounts; SDK script deduplication does not deduplicate chat.load() calls. A reused SDK still needs initialization after an intentional widget teardown. Keep credentials out of public environment variables and serialized props; only the generated browser verification belongs in client code.

Customize and check

Keep brand overrides beside this one configuration; use customization for remote settings and supported CSS. A custom support button should invoke the existing ready widget, not mount another one.

Test a cold deep link, development remount, production build, route changes, Back, and a slow SDK load followed by navigation. Confirm one widget and expected conversation persistence. For authenticated sites, also check logout/account switch without leaking the previous conversation. Roll back the integration component and its imports/listeners, retaining unrelated layout code.

Sources: React effects, Next.js scripts.