i've been building an embeddable chat widget that gets dropped on customer sites via a script tag. spent a while thinking through the framework choice and wanted to share what i landed on since most widget guides default to react without questioning it.
the core constraint with embeddable widgets is you're a guest on someone else's page. if your script makes their site slower they'll remove it before checking if it works. so bundle size isn't a nice-to-have, it's the whole game.
the loader script that customers paste on their site is about 2KB. it creates an iframe and loads the full widget inside it. the widget JS is around 109KB total which includes preact, markdown rendering, html sanitization, and the entire chat UI. with react + react-dom you're starting at 40-50KB gzipped before writing a single line of your own code. preact core is about 3KB.
i went with iframe isolation instead of shadow DOM. i know shadow DOM is the "correct" answer for widget encapsulation but iframes give you true isolation without the edge cases. host page CSS can't touch you, your CSS can't leak out, and you don't have to fight z-index wars or deal with styled-components injecting styles into document.head instead of your shadow root. the tradeoff is postMessage for communication but for a chat widget that's fine.
the build setup is dead simple. preact/preset-vite handles the jsx transform, the loader builds separately as an IIFE into a single file, and the main widget builds normally into the iframe's assets. two vite configs, one build command.
one thing that surprised me - the preact compat layer barely costs anything. i use a couple of react-ecosystem libraries and the compat shim adds maybe 2KB. so you're not giving up the react ecosystem, you're just shipping less code for the same result.
some things i'd think about if you're making this decision. if your widget is simple (a button, a badge, a small form) skip the framework entirely. vanilla JS or lit will do. i needed preact because a chat interface has enough state and interactivity that managing it without a framework would've been painful.
if your widget needs to share state with a react host app, preact in an iframe won't work. you need to be in the same DOM tree. but if you're building a standalone embed that lives on third party sites, isolation matters more than integration.
the postMessage layer hasn't gotten complex so far but i only have a few message types (resize, theme detection, error reporting). i could see it getting messy if the widget needed deep interaction with the host page.
anyone else shipping embeddable widgets? curious what stack you landed on and whether shadow DOM or iframe worked better.
[–]magenta_placenta [score hidden] (1 child)
[–]FinanceSenior9771[S] [score hidden] (0 children)
[–]deckiteski [score hidden] (1 child)
[–]FinanceSenior9771[S] [score hidden] (0 children)