None of these are exotic. All four account for nearly every “the widget is not showing” report from a React codebase.
01The double mount in strict mode
React 18 runs effects twice in development. Without a guard you get two script tags and two launchers, which then look like a widget bug rather than a mounting bug. Query for the existing tag by data-bot-id before appending.
02Client-side navigation
The widget lives in document.body, not in your component tree, so a route change does not disturb it. That is what you want — a chat that resets on every navigation is worse than no chat. It also means unmounting the component does not remove the widget.
03Content Security Policy
If you send a CSP, the script host has to be allowed in script-src, and the widget’s network calls in connect-src. A silent CSP block is the single most common reason a correctly placed tag does nothing; check the console before anything else.
04Server-side rendering
Nothing here runs on the server. next/script handles the client boundary itself, and the useEffect version never executes during SSR — so no document is touched where document does not exist.