Skip to content
Docs

Widget Performance and Core Web Vitals

How the beyondRegular widget loads: async script, ~26 KB gzipped, no LCP block. Tips to keep LCP, CLS, and INP green on your storefront.

Widget Performance and Core Web Vitals
SetupWidget Performance and Core Web Vitals

Drop the async loader in the head or before body-end, and place the mount div where you want the widget. The bundle is around 26 KB gzipped and loads out of band, so it does not block LCP. To keep CLS at zero, reserve height on the mount div until the widget paints.

The exact install (paste this)

Two tags. One loader, one mount point. The loader is async so the browser never waits on it before painting your hero image or headline.

```html <script async src="https://www.itsbeyondregular.com/embed.js"></script> <div data-bn-preset="PRESET_ID" style="min-height:520px"></div> ```

Replace PRESET_ID with the id from your dashboard. The min-height on the mount div is the CLS trick: it holds the space so when the widget hydrates, nothing below it jumps. Pick a height close to what the widget will render at on desktop; mobile reflow is handled by the widget itself.

On Shopify, skip the script tag. Turn on the theme app embed in the theme editor and drop the block where you want it. The loader is injected once per page.

What the numbers mean for LCP, CLS, INP

LCP: the loader is async and the mount div is empty until hydration, so the widget cannot become your Largest Contentful Paint. Your hero image or H1 stays the LCP element. The bundle sits around 26 KB gzipped against a 30 KB budget, fetched off the critical path.

CLS: zero if you reserve space. If you leave the mount div at height 0 and let it grow when the widget paints, you will ship a layout shift. Use min-height, or wrap the div in a container with a fixed aspect-ratio.

INP: the widget uses passive listeners and defers video decode until a tile is in view. Tapping a tile opens the player in the same frame; no long tasks past 200 ms in our measurements on a mid-tier Android.

Placement rules that actually matter

Above the fold is fine. The loader is async, the mount div is empty until hydration, and the widget uses IntersectionObserver to only decode video for tiles that are visible. Putting the carousel high on a product page does not hurt LCP.

Do not lazy-load the mount div with loading="lazy" hacks or display:none until scroll. That defeats the widget's own viewport logic and delays first paint of the tiles.

One preset per page is the happy path. You can mount two presets on the same page (e.g. a horizontal carousel up top and a reel feed lower down) and embed.js will dedupe itself, but every extra preset adds product-catalogue fetches. Keep it to two.

Preloading and CSP

If you want a small win on repeat visits, add a preconnect. It shaves the TLS handshake for the first tile fetch. Do not preload the script itself; async already gets it out of the critical path.

```html <link rel="preconnect" href="https://www.itsbeyondregular.com" crossorigin> ```

For Content Security Policy, allow the origin on script-src, connect-src, img-src, and media-src:

``` script-src 'self' https://www.itsbeyondregular.com; connect-src 'self' https://www.itsbeyondregular.com; img-src 'self' https://www.itsbeyondregular.com data:; media-src 'self' https://www.itsbeyondregular.com blob:; ```

Troubleshoot: widget is slow or shifts

Ordered by how often we see it. First: no min-height on the mount div. Fix by reserving space as shown in the install snippet. Second: the loader was moved inside a tag manager container that fires on window load. Move it back into the page HTML with async so it starts fetching during HTML parse.

Third: too many presets on one page. Each preset triggers its own catalogue call. Cap at two per page. Fourth: heavy hero video on the same page competing for bandwidth on 3G. The widget will still paint, but tile thumbnails arrive later. Move the hero to a poster image with click-to-play.

Fifth: an ad blocker or strict CSP is dropping the script. Check DevTools Network for a blocked request to embed.js and add the origin to your CSP allowlist.

Common questions

Will the widget hurt my Lighthouse or Core Web Vitals score?

No, if you install it as documented. The loader is async so it does not delay LCP, and the mount div stays empty until hydration so it cannot become the LCP element itself. The bundle is around 26 KB gzipped, fetched off the critical path. The one thing you must do is reserve height on the mount div with min-height or an aspect-ratio wrapper. If you skip that, the widget will push content down when it paints and your CLS will go red. Everything else, including video decode, is deferred until a tile is in the viewport.

Can I self-host embed.js or bundle it with my app?

No, and you do not want to. The loader at https://www.itsbeyondregular.com/embed.js is a thin bootstrap that pulls the current widget chunk, product catalogue data, and preset config. Self-hosting would pin you to an old version and break analytics and Shopify web pixel attribution. It would also cost you the shared HTTP cache across sites already using the loader, which is a real repeat-visit win. Keep the script tag pointing at our origin; the bundle behind it is versioned and cached at the edge.

Does the widget block first paint on slow connections?

The script tag is async, so parsing your HTML and painting your hero is never blocked by embed.js. On a slow 3G profile, the loader still fetches in parallel with your own assets. The widget's own tiles load progressively: poster images first, video only when a tile enters the viewport. If your page paints slowly on slow connections, the widget is almost never the cause; check your hero image size and your own render-blocking CSS first. Run a Lighthouse mobile audit with the widget mounted and you can see the trace for yourself.

Put shoppable video on your store.

7-day free trial. No card needed.

Start free trial