Site-wide widgets on any HTML or React site
On a site you control, a site-wide widget is the simplest install of the six: put the snippet just before the closing body tag of whatever template every page shares — a base layout in Jinja, Blade, ERB or Handlebars, `_layouts/default.html` in Jekyll, `layouts/default.vue` in Nuxt, `app/layout.tsx` in Next.js, or plain `index.html` in a single-page app. There is no plan gate and no panel to find. What is worth reading is the two paragraphs below on single-page applications and on Content Security Policy, because those are the only two ways this goes wrong on a hand-built site, and neither produces an obvious error message.
Build and style the widget in the Zyff widget builder, then follow the any HTML or React site steps below to put it on every page.
How to add a site-wide widget to an HTML or React site
Build the widget and copy the code
Set the widget up in any Zyff builder and copy the embed code — one <div> and one <script> tag.
Find the layout every page shares
A base template in a server-rendered app, app/layout.tsx in the Next.js App Router, layouts/default.vue in Nuxt, _layouts/default.html in Jekyll, or index.html in a Vite or Create React App build. Anything included by every page will do.
Paste just before </body>
Put the container div and the script tag together at the very bottom, before the closing body tag. The script is loaded async, so it never blocks rendering, and a floating widget positions itself — the div's position in the document does not matter.
In React, render it as ordinary markup
No wrapper component, no useEffect, no next/script needed. The div and the script tag are plain JSX in your root layout. The runtime watches the document for containers added later, so it also works if you render the div from a component.
Check the console once
Load a page and open the browser console. The widget fails silently by design — an error box on a real site is worse than an absent widget — so a single [zyff] warning in the console is how you tell the difference between “broken” and “blocked”.
Things worth knowing on a hand-built site
Client-side routing is already handled
The runtime watches the document with a MutationObserver, so a container added by a later route change is picked up and mounted without the script being re-run. React Router, the Next.js App Router, Vue Router and Astro view transitions all work with no extra code.
Content Security Policy is the usual blocker
If your site sends a CSP, script-src must allow https://www.zyff.app or nothing loads. There is no visible error on the page — only a console message from the browser, which is why this is the first thing to check when a hand-built site shows nothing.
Including the script twice is safe
The runtime marks each container as it mounts and skips anything already handled, so a duplicated script tag does not produce two widgets. Two separate containers, however, deliberately produce two widgets — that is how you put a chat button and a back-to-top button on the same page.
It renders into a shadow root
Your stylesheet cannot reach inside the widget and the widget cannot leak out. That is what makes it safe on a site with aggressive global CSS — and it is why the widget's own colours and sizes are set in the builder rather than overridden with your CSS.
Static site generators need nothing extra
Jekyll, Hugo, Eleventy, Astro and Gatsby all emit ordinary HTML. Put the snippet in the shared layout and it ships with every generated page — there is no plugin or integration to install.
The script is order-independent
It can appear before or after the container div and works either way, so you can keep every third-party script together at the bottom of the layout rather than interleaving them with markup.
Widgets that belong on every page
Frequently asked questions
Where do I put the script on a hand-built site?
Just before the closing </body> tag of whatever layout every page shares — a base template, app/layout.tsx in Next.js, layouts/default.vue in Nuxt, _layouts/default.html in Jekyll, or index.html in a single-page app.
Do I need next/script or a useEffect wrapper in React?
No. Write the div and the script tag as ordinary JSX in your root layout. The runtime also watches for containers added later, so rendering the div from a component works too.
Does it survive client-side navigation?
Yes. A MutationObserver watches the document, so a container that appears after a route change is mounted without reloading the script. That covers React Router, the Next.js App Router, Vue Router and view transitions.
Nothing renders and there is no error.
Check the browser console. The widget fails silently on purpose — an error box on a live site is worse than an absent widget — so a [zyff] warning there is the diagnostic. A blocked request with no [zyff] warning usually means Content Security Policy.
What do I need to add to my Content Security Policy?
https://www.zyff.app in script-src, so the runtime can load. A saved widget also fetches its configuration from the same origin, so connect-src needs it too; a snippet with its settings inlined makes no request at all.
Can I style it with my own CSS?
Not from outside — it renders into a shadow root, which is deliberate and is what stops a site's global styles from breaking it. Set colours, sizing and typeface in the Zyff builder instead.