The empty-div problem nobody's pricing page mentions
Point a typical "train on your website" chatbot at a client-rendered site and its crawler fetches your HTML the way curl would: before React mounts, before Vue hydrates, before your no-code builder injects content. What it indexes is <div id="root"></div> and a bundle URL. The chatbot then confidently knows nothing about your product — a failure mode that only shows up after you've onboarded, uploaded nothing (because "it learns your site!"), and watched it fumble your first real question.
Three ways vendors work around it — and their costs
- Headless-browser crawling: some crawlers render JS in a headless browser. It helps, but it's slow, quota-capped (page limits per tier), misses login-gated and interaction-revealed content, and still runs on a schedule — your site changes, the bot lags until the next sync.
- Manual uploads: exporting your content to PDFs/markdown and uploading it. Works, but now you own a content-sync chore forever.
- Passive DOM learning (Flocci Chat's approach): the embedded widget reads the fully-rendered DOM — on load and on SPA route changes — and sends it to the knowledge base. Every page a real visitor opens is learned exactly as rendered; changed pages are re-learned and replace the old version on the next visit. Coverage tracks real traffic automatically, with zero page caps and zero sync schedule.
What this means in practice for an SPA
An onboarding crawl still covers whatever is server-reachable on day one. Then, within about 24 hours of normal traffic, passive learning has captured your actual surface — including routes only reachable by client-side navigation. To bootstrap instantly, just browse your own site for five minutes after embedding: every page you open is ingested on the spot. Private, logged-in surfaces stay out with a single attribute: data-passive-ingest="off".
Does the widget itself behave well inside an SPA?
Yes — it's built for hostile environments: Shadow-DOM encapsulated (your CSS can't break it, it can't break yours), loads async/defer, survives client-side route changes, and fails silently rather than throwing into your console. It's the same widget running on this page right now — this site is itself a static + JS-rendered mix that Flocci Chat learned automatically.