Kestero

Where to paste the snippet

Last updated 31 August 2026

The short version

Your widget gives you one line of HTML — a <script> tag. It goes into your site’s HTML once, not once per page, and it works anywhere in the document. Most platforms have a box for exactly this, usually called something like “custom code”, “code injection” or “header and footer scripts”.

It loads asynchronously, so it never blocks your page from rendering, and it does nothing at all until the trigger you chose fires.

Any site you can edit the HTML of

Paste it just before the closing </body> tag. It works in <head> too — the script is async either way — but the end of the body is the conventional place and the easiest to find again later.

<body>
  <!-- your page -->

  <script src="https://kestero.com/widget.js" async
    data-api="https://kestero.com"
    data-key="ke_your_key"
    data-widget="your-widget-id"></script>
</body>

Copy the real one from your widget’s page — the key and the id above are placeholders.

Site builders and CMSs

WordPress

Look for a theme option named header and footer scripts, or use a small plugin that adds one (WPCode and Insert Headers and Footers are the common choices). Paste into the footer box.

Avoid pasting into a post or page with the block editor — WordPress strips script tags from post content unless you are an administrator, and it will look like the snippet vanished.

Shopify

Online Store → Themes → Edit code, then open theme.liquid and paste just before </body>. Duplicate your theme first if you have not edited code before.

On Shopify Plus you can use the checkout scripts area instead, but the theme is the right place for a widget that should appear on the storefront.

Squarespace

Settings → Code injection → the Footer box. This applies to every page of the site, which is what you want for a single paste.

Code injection is a paid-plan feature on Squarespace. If you cannot find it, that is usually why rather than a missing menu.

Wix

Settings → Custom code → add code to Body – end, applied to all pages, loaded once.

Webflow

Site settings → Custom codeFooter code, then publish. Custom code does not run in the Designer preview — you have to publish and look at the live site.

Framer

Site settings → General → custom code → End of body. As with Webflow, publish before testing.

Ghost

Settings → Code injectionSite footer.

Google Tag Manager

A Custom HTML tag, firing on All Pages. Works well, with one caveat worth knowing: if a visitor blocks GTM, they get no widget — so if the widget is important to you, paste it into the site directly instead.

React, Next.js, Vue and other frameworks

Add the tag to whatever renders your outermost HTML, so it mounts once for the whole app rather than on every route change. In Next.js that is your root layout, using next/script:

import Script from "next/script";

<Script
  src="https://kestero.com/widget.js"
  strategy="afterInteractive"
  data-api="https://kestero.com"
  data-key="ke_your_key"
  data-widget="your-widget-id"
/>

In a plain React or Vue app, put the script tag in index.html rather than inside a component. A component that mounts and unmounts would load the widget more than once.

Telling the widget who is looking

Sometimes a widget should not appear for certain people — a banner selling your paid plan has no business following somebody who already pays for it. Only your site knows that, so your site is the one that says it.

Add data-signal-* attributes to the embed tag, with whatever your server already knows about the person on the page:

<script src="https://kestero.com/widget.js" async
  data-api="https://kestero.com"
  data-key="ke_your_key"
  data-widget="your-widget-id"
  data-signal-plan="pro"
  data-signal-signedin="true"></script>

Then in the builder, under When it appears, set the rule to plan != "pro". Names are yours to choose — whatever you send is what you compare against. true and false are read as booleans and numbers as numbers, so credits > 0 works too, and and / or / not combine them.

None of this reaches us. The signals are read in the visitor’s browser, compared there, and thrown away — they are not sent with the widget request, not stored, and not attached to anything. Your site has told the widget one fact about the person already in front of it, and nothing more leaves the page than did before.

A signal you have not set yet leaves the widget showing rather than hiding it. That is deliberate: forgetting an attribute should not silently empty a funnel, and a widget appearing to somebody it should not is the mistake you notice.

If nothing appears

Roughly in the order these actually happen:

  • The widget is a draft. A draft renders nothing on purpose — switch it live on its page in the dashboard.
  • The trigger has not fired yet. A widget set to appear after ten seconds, on exit, or at a scroll depth is working correctly by doing nothing until then.
  • The page was not republished. Webflow, Framer and Squarespace all need a publish before custom code runs on the live site.
  • It was pasted into page content rather than into site code. Most CMSs strip script tags out of the body of a post or page.
  • Your site has a Content Security Policy. If you set one, it needs to allow scripts and frames from kestero.com — most sites have no CSP and nothing to change.

A widget set to render inline also needs the element it mounts into to exist on the page. If the CSS selector matches nothing, there is nowhere for it to go and nothing is drawn.

A few things worth knowing

  • One paste per site, not per page. The snippet is site-wide; where a widget appears is decided by its trigger and its rules, not by which pages you paste it on.
  • It works on any domain by default. There is no allow-list to configure before your first install, and nothing to change when you move from a staging domain to a real one.
  • The data-widget attribute pins the snippet to one widget. Remove it and the key renders every live widget on that project instead.
  • Nothing is loaded until it is needed — a widget with no code block never downloads the sandbox, and the whole renderer is a fraction of a typical image.

Still stuck

Send us the page it is meant to be on and we will look at it — get in touch. The URL is usually enough to see what is happening.


Questions about any of this? Get in touch.