Skip to main content

Embed storefront widgets

Two paths

PathUse when
Theme App ExtensionStandard Shopify Liquid theme
Custom embed (public APIs)Hydrogen, Next.js, custom React, headless

Path 1 — Theme App Extension (Shopify Liquid)

The simplest path. Sumeru publishes a Theme App Extension your store can install with one click.

Install

  1. Shopify admin → Apps → Sumeru Systems → Theme extensions
  2. Click Add to theme
  3. Theme Customizer opens; widgets appear as available blocks

Toggle widgets

In Theme Customizer:

  • Open the section you want (e.g. Product page)
  • Click Add block → select widget (e.g. Stock counter)
  • Configure widget options
  • Save

That's it. The widget renders on the live storefront, auto-loads its data, sends events back to Sumeru.

Available widgets

See merchant docs for the full catalog.

Path 2 — Custom embed (public APIs)

For storefronts not on Shopify Liquid (Hydrogen, Next.js, custom). Build the UI yourself; consume Sumeru public APIs.

Setup

In your storefront's HTML head:

<script src="https://pixel.sumeru.systems/v1/p.js"
data-shop="yourshop.com"
async></script>

This loads the pixel + makes window.Sumeru available for tracking.

Example: stock counter

// React component
import { useEffect, useState } from 'react';

export function StockCounter({ productId }) {
const [stock, setStock] = useState(null);

useEffect(() => {
fetch(`https://api.sumeru.systems/api/public/product-stock?product_id=${productId}`, {
headers: { 'X-Shop-Domain': 'yourshop.com' }
})
.then(r => r.json())
.then(data => setStock(data.data.available));
}, [productId]);

if (stock === null || stock > 15) return null; // Don't show if plenty
return <p className="stock-counter">Only {stock} left</p>;
}

Example: reviews block

import { useEffect, useState } from 'react';

export function ReviewsBlock({ productId }) {
const [reviews, setReviews] = useState([]);

useEffect(() => {
fetch(`https://api.sumeru.systems/api/public/reviews?product_id=${productId}&limit=10`, {
headers: { 'X-Shop-Domain': 'yourshop.com' }
})
.then(r => r.json())
.then(data => setReviews(data.data));
}, [productId]);

return (
<section className="reviews">
{reviews.map(r => (
<article key={r.id}>
<strong>{r.author_name}</strong>{r.rating}
<p>{r.body}</p>
</article>
))}
</section>
);
}

Example: capture widget

import { useState } from 'react';

export function CapturePopup() {
const [email, setEmail] = useState('');
const [submitted, setSubmitted] = useState(false);

async function submit(e) {
e.preventDefault();
await fetch('https://api.sumeru.systems/api/public/opt-in', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Shop-Domain': 'yourshop.com' },
body: JSON.stringify({
email,
channel: 'email',
source: 'exit-intent-popup',
consent_text: 'Get 10% off your first order — sign up for our newsletter.',
}),
});

// Identify in pixel
window.Sumeru?.identify({ email });

setSubmitted(true);
}

if (submitted) return <p>Thanks! Check your email for your code.</p>;
return (
<form onSubmit={submit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="your@email.com"
required
/>
<button type="submit">Get 10% off</button>
</form>
);
}

Example: cart upsell

import { useEffect, useState } from 'react';

export function CartUpsell({ cartItems }) {
const [suggestions, setSuggestions] = useState([]);

useEffect(() => {
fetch('https://api.sumeru.systems/api/public/upsell', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Shop-Domain': 'yourshop.com' },
body: JSON.stringify({ items: cartItems }),
})
.then(r => r.json())
.then(data => setSuggestions(data.data.suggestions));
}, [cartItems]);

return (
<section className="upsell">
<h3>You may also like</h3>
{suggestions.map(s => (
<a key={s.product_id} href={`/products/${s.handle}`}>
<img src={s.image_url} alt={s.title} />
<span>{s.title} — ${s.price}</span>
</a>
))}
</section>
);
}

Tracking custom events

// On any user action
window.Sumeru?.track('newsletter_signup', {
source: 'footer'
});

// On cart add
window.Sumeru?.track('add_to_cart', {
product_id: 'prd_123',
variant_id: 'var_456',
quantity: 1,
price: 49.99
});

Trade-offs

Theme App ExtensionCustom embed
Speed to install30 sechours-days
MaintenanceNone (Sumeru updates centrally)You maintain
CustomizationTheme Customizer optionsFull HTML/CSS control
PerformanceSingle bundle, optimizedWhatever you build
TradeSpeedControl

For most stores: Theme App Extension is the right answer. For headless / custom platforms: only the custom-embed path works.

CSP requirements

If you have a strict Content Security Policy:

script-src https://pixel.sumeru.systems 'self';
connect-src https://api.sumeru.systems 'self';
img-src https://cdn.sumeru.systems https: data:;

Auth

For default storefront use (CORS-allowed domain): no auth needed.

For headless / non-allow-listed domains: include X-Public-Key: pk_live_... header.

Rate limits

Public endpoints have generous rate limits per shop + per-IP. See Rate limits. Cache reads at edge to stay well under.

See also