Developer
Integrating the Bird Widget in a Shopify Headless Storefront
The Bird Pickup & Delivery scheduler runs on any storefront — Hydrogen, React, Vue, or plain HTML. You add one stylesheet and one script, set your shop domain, and call mount(). There is no theme app extension, no build step, and no npm package to install.
See it running: the live demo storefront mounts the real widget on a mock product page and cart, with a copy-paste configuration reference. It’s the fastest way to confirm the exact behavior before you wire it into your own build.
Requirements
- The Bird Pickup & Delivery app must be installed and enabled in your Shopify admin.
- You need your
*.myshopify.comdomain (for exampleyour-store.myshopify.com).
That’s it — the widget fetches its settings, translations, timezone, and availability rules automatically from your Bird configuration.
Quick start
Add the stylesheet, set window.BirdApp.config, then load the bundle. The config must be set before the bundle runs.
<!-- 1. styles + config, then the bundle -->
<link rel="stylesheet" href="https://cdn.birdchime.com/widget/bird.bundle.css">
<script>
window.BirdApp = { config: { shop: 'your-store.myshopify.com' } };
</script>
<script src="https://cdn.birdchime.com/widget/bird.bundle.js" defer></script>
Then, once the bundle is ready, mount the widget into a container element and keep it fed with your cart:
// 2. mount once ready (mount is async)
await window.BirdApp.ready;
const handle = await window.BirdApp.widget.mount(el);
// 3. keep the cart in sync — pass your Storefront cart as-is
window.BirdApp.sdk.setCart(cart);
The window.BirdApp object
The bundle exposes a single global with four members. You set config; the bundle fills in the rest.
window.BirdApp = {
config, // you set this — { shop, locale?, customer? }
ready, // Promise<sdk | null> — await before mounting
sdk, // BirdSDK instance (or null) — sdk.setCart(cart)
widget, // { mount(container, options?) }
}
Configuration
Set window.BirdApp.config before the bundle loads. Only shop is required.
| Field | Required | Notes |
|---|---|---|
shop |
Yes | Your *.myshopify.com domain |
locale |
No | Defaults to 'en'
|
customer |
No |
{ id, tags, loggedIn } — needed only if the merchant uses customer-tag availability rules |
Mounting the widget
Call widget.mount(container, options?), which returns a Promise<MountHandle | null>.
-
Cart widget (default) — mount with no options. It reads the cart you provide via
sdk.setCart.const cart = await window.BirdApp.widget.mount(cartEl); -
Product widget — pass
{ type: 'product', item }, whereitemdescribes the product/variant being viewed. The item’stagsandcollectionIdslet per-product availability rules apply.const product = await window.BirdApp.widget.mount(productEl, { type: 'product', item: { productId, variantId, sku, quantity: 1, price, tags: [], collectionIds: [], }, });
The returned handle is { destroy(), isMounted(), remount? }. It resolves to null if mounting couldn’t complete — check the browser console.
A selection made on the product page carries into the cart automatically, because both widgets share one sdk instance.
Keeping the cart in sync
Call sdk.setCart(cart) on load and whenever the cart changes (add, remove, quantity update). Pass your Storefront-API cart object as-is — the widget reads the line items, totals, and attributes it needs from it.
window.BirdApp.sdk.setCart(cart);
Hydrogen / React example
window.BirdApp exists only in the browser, so mount inside an effect that runs after render — never during server render.
import { useEffect, useRef } from 'react';
function BirdCartWidget({ cart }) {
const ref = useRef(null);
useEffect(() => {
let handle;
(async () => {
const sdk = await window.BirdApp.ready;
if (!sdk) return; // init failed — see console
sdk.setCart(cart);
handle = await window.BirdApp.widget.mount(ref.current);
})();
return () => handle?.destroy();
}, []);
// push cart changes into the widget
useEffect(() => {
window.BirdApp?.sdk?.setCart(cart);
}, [cart]);
return <div ref={ref} />;
}
Notes
-
Browser-only.
window.BirdAppis client-side only. Never reference it during server render — in Hydrogen, mount inside auseEffect. -
readynever rejects. On init failure it resolves tonull(andmount()then resolves tonull). Check fornull— don’t rely on.catch(). -
Non-blocking. The bundle loads with
deferand is served from a CDN, so it never blocks your page render.
Related Articles
Was this article helpful?
Thanks for your feedback!