@numueg/theme-sdk — the React hooks + components every NUMU theme consumes.Install#
Themes get the SDK as a peer dep (externalized at build time, resolved by the storefront's import map at runtime):{
"peerDependencies": {
"@numueg/theme-sdk": "^0.6.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
Themes import via bare specifier:import { useCart, AddToCartButton, Money } from "@numueg/theme-sdk";
What the SDK provides#
Hooks (25+)#
| Hook | Purpose |
|---|
useShop() | Store name, currency, domain helpers |
useThemeSettings() | Resolved customizer state |
useLocalization(), useDirection(), useTranslation() | i18n + RTL |
useCurrency() | Multi-currency presentment |
usePage() | Current page type + data (route-aware) |
useProduct(), useProductOptional() | Product context |
useProducts() | Paginated product list |
useCollection(), useCollectionOptional(), useCollections() | Categories |
useVariantSelection(product) | PDP variant picker state machine |
useCart() | Cart contents + mutations |
useCustomer() | Logged-in customer or null |
useCustomerActions() | login/register/logout/recover/reset |
useCustomerAddresses() | Address book CRUD |
useOrders(), useOrder() | Order history |
useReorder() | "Buy again" |
useGiftCardBalance() | Checkout-step gift card probe |
useCheckout() | Programmatic multi-step checkout driver |
useShippingRates() | Resolve shipping options for an address |
useNavigation(handle) | Merchant-defined nav menus |
useSearch() | Predictive + full search |
useAnalytics() | track(event, payload) |
useRelatedProducts(productId) | Same-category siblings |
useWishlist() | Wishlist persistence |
useApp(slug) | App-provided data (Phase 9 platform) |
useSection(), useSectionOptional() | Current section context (settings + blocks) |
useImage(), useMoney() | Formatting helpers |
Detailed reference: Hooks.Components (15+)#
| Component | Purpose |
|---|
<NuMuProvider> | Root provider — wraps the theme tree |
<ProductProvider>, <CollectionProvider> | Override context for nested trees |
<Section>, <Block> | Render sections + blocks defined in schemas |
<Image> | Lazy + responsive + content-hashed srcset |
<Money> | Currency formatter that respects useCurrency |
<Link> | Internal link with subdomain awareness |
<Form> | Server-action form with progressive enhancement |
<AddToCartButton> | Variant-aware add-to-cart with built-in UX states |
<ProductCard> | Opinionated default card |
<CollectionCard> | Opinionated default card |
<RichText html> | DOMPurify-sanitized HTML renderer |
<CurrencySwitcher>, <LocaleSwitcher> | Storefront toggles |
<CookieConsent> | Cookie consent banner (GDPR, Phase 5.6) |
Utilities#
findVariantByOptions(product, selection) — variant resolution
defaultVariant(product) — first in-stock variant or fallback
availableValues(product, selection) — which axis values still resolve to in-stock variants
assetUrl("path") — resolve a theme asset URL (content-hashed)
sanitizeHtml(html) — same DOMPurify config <RichText> uses
resolveThemeSettings(raw) — legacy field normalizer (rarely needed in themes)
registerSdkSingleton, getSdkSingleton — federation runtime helpers (internal)
The minimum theme#
// src/main.tsx
import { defineThemeEntry, usePage, useShop } from "@numueg/theme-sdk";
function App() {
const shop = useShop();
const page = usePage();
return (
<div>
<h1>{shop.name}</h1>
<p>Current page: {page.type}</p>
</div>
);
}
// defineThemeEntry wraps <NuMuProvider> (plus catalog, navigation, and
// global style tokens) around your app and returns BOTH entry points the
// host calls — you never touch createRoot or the mount container yourself:
// mount(el, ctx) — client mount/hydrate into the host-supplied element
// createApp(ctx) — server render (renderToString) for SSR
const entry = defineThemeEntry(() => <App />);
export const mount = entry.mount;
export const createApp = entry.createApp;
That's a valid, installable theme. The customizer will show it. It renders nothing useful — but numu-theme dev previews it, numu-theme build compiles it, numu-theme submit ships it. Because it exports createApp, the host can server-render it too.Conventions used throughout#
*Optional() variants#
Hooks like useProductOptional() return null instead of throwing when no context is in scope. Use these in sections that might render outside a <ProductProvider> (e.g. a quick-add card on a collection page).// Throws when used outside ProductContext:
const product = useProduct();
// Returns null instead:
const product = useProductOptional();
if (!product) return <div>No product in context</div>;
Money is in cents#
unit_price, total, subtotal, amount — all in integer minor units (cents, piasters). Divide by 100 for display, or use <Money> / useMoney().Snapshot-vs-live prices#
For cart line items, unit_price is the snapshot at add-time and current_price is the live price. Use the snapshot for the order amount; show "price changed" UI when price_changed is true.Variant-aware everything#
When a product has variants, the cart line, order line, and checkout payload all carry a variant_id. The SDK's variant helpers normalize the API surface so you don't have to special-case "products with variants" vs "products without."Importing from sub-paths#
The SDK exposes only the top-level @numueg/theme-sdk for now. There's no @numueg/theme-sdk/hooks or @numueg/theme-sdk/components. Tree-shaking handles unused code in your bundle.The single exception is the JSX runtime (used internally by the federation runtime):"@numueg/theme-sdk/jsx-runtime"
Themes never import this directly.TypeScript#
Strict mode + tsconfig strict: true is recommended. The SDK ships with full .d.ts files. Common gotchas:useCart() returns { cart, addItem, removeItem, updateQuantity, applyDiscount, removeDiscount, updateNote, clearCart, loading } — destructure what you need.
useCustomer() returns Customer | null — branch on it.
usePage() returns a discriminated union by page.type — TypeScript narrows after a switch.
Versioning#
Patch — Bug fixes, no surface change
Minor — New hooks/components, additive prop changes
Major — Removed/renamed exports, prop signature changes
Themes pin via peerDependencies with ^x.y.z so patch + minor updates auto-apply. Major bumps require a theme code change.Modified at 2026-09-24 13:03:12