Embed storefront widgets
Two paths
| Path | Use when |
|---|---|
| Theme App Extension | Standard 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
- Shopify admin → Apps → Sumeru Systems → Theme extensions
- Click Add to theme
- 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 Extension | Custom embed | |
|---|---|---|
| Speed to install | 30 sec | hours-days |
| Maintenance | None (Sumeru updates centrally) | You maintain |
| Customization | Theme Customizer options | Full HTML/CSS control |
| Performance | Single bundle, optimized | Whatever you build |
| Trade | Speed | Control |
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.