NUMU Docs
Contact
APIThemes
Partner Apps
APIThemes
Partner Apps
  1. Themes
  • šŸ—‚ļø Themes overview
  • šŸš€ Start here
  • Glossary
  • Theme engine
    • Architecture
    • BYOT contract
    • Customizer
    • Federation runtime
    • Page data contract
    • Theme manifest
  • Theme SDK
    • Components
    • Federation helpers
    • Hooks
    • SDK overview
    • Type definitions
  • CLI & Vite plugin
    • CLI commands
    • CLI overview
    • Lint rules
    • Section library
    • Vite plugin
  • Storefront host
    • API proxies
    • Built-in fallbacks
    • BYOT fork
    • Routing
    • Storefront overview
  1. Themes

šŸš€ Start here

End-to-end: from numu-theme init to a customer rendering your theme on a real store.

Stages#

1. INIT           scaffold local theme
2. DEV            iterate with HMR
3. LINT + CHECK   static validation
4. BUILD          produce dist/
5. PUSH           upload to your dev sandbox
6. INSTALL        install your pushed theme into your test store
7. SUBMIT         submit for public marketplace review
8. REVIEW         admin diff + approve
9. PUBLISH        bundle goes to R2; marketplace listing public
10. MERCHANT INSTALL  merchant picks your theme; live at <sub>.numueg.app
You'll iterate stages 2 → 4 many times before submitting. Stage 5 + 6 are the "test on a real backend" loop.

1. Init#

Result:
my-fashion-theme/
ā”œā”€ā”€ package.json           ← peerDeps: react, react-dom, @numueg/theme-sdk
ā”œā”€ā”€ vite.config.ts         ← @numueg/theme-plugin pre-wired
ā”œā”€ā”€ tsconfig.json
ā”œā”€ā”€ theme.json
ā”œā”€ā”€ settings_schema.json
ā”œā”€ā”€ src/
│   ā”œā”€ā”€ main.tsx
│   ā”œā”€ā”€ pages/{Home,Product,Cart,NotFound}.tsx
│   ā”œā”€ā”€ sections/Hero.tsx
│   └── blocks/CtaButton.tsx
ā”œā”€ā”€ schemas/
│   ā”œā”€ā”€ sections/hero.json
│   └── blocks/cta-button.json
ā”œā”€ā”€ locales/
│   ā”œā”€ā”€ en.default.json
│   └── ar.json
ā”œā”€ā”€ templates/
│   ā”œā”€ā”€ error.html
│   └── loading.html
└── assets/
Open in your editor.

2. Dev#

→ http://localhost:3001/__numu/preview renders your theme against mock data. Edits in src/ hot-reload.
To preview against a real merchant's customizer state:
1.
Set the merchant's external_theme.bundle_url in the hub (or via API) to http://localhost:3001/theme.js
2.
Open the merchant's hub customizer
3.
The iframe loads your local bundle — every theme change reflects instantly
Two terminals
Run numu-theme dev in one tab, your editor in another. The plugin auto-runs schema codegen on save — you don't need to restart Vite.

3. Lint + check#

Address every error. Warnings are usually informational ("you have 3 unused settings_schema entries") but they accumulate technical debt.

4. Build#

Output: dist/.
dist/
ā”œā”€ā”€ theme.js           ← the bundle
ā”œā”€ā”€ theme.css
ā”œā”€ā”€ manifest.json      ← integrity hashes + metadata
ā”œā”€ā”€ sections.json      ← schema index
ā”œā”€ā”€ asset-manifest.json
ā”œā”€ā”€ assets/
│   └── <hash>.<ext>
ā”œā”€ā”€ error.html         ← copied from templates/
└── loading.html
Inspect:

5. Push#

Uploads dist/ to your developer sandbox under your marketplace account. The theme is NOT publicly listed yet — only you can install it.
Behind the scenes:
The CLI zips dist/ + the source (so the marketplace can re-build server-side later if needed)
POST to /marketplace/themes/{id}/versions with Authorization: Bearer <cli token>
The backend stores the version under marketplace_theme_versions with status=developer_uploaded
Returns the version ID + a temporary install URL

6. Developer-install#

Installs into your test store (the one linked to your marketplace account at signup).
Behind the scenes:
/marketplace/themes/{id}/install sets store.theme_settings.external_theme.bundle_url to your sandbox URL
The customizer state resets to your theme.json presets
The next storefront request renders your bundle
Open https://<your-test-subdomain>.numueg.app/ — your theme is live.
Developer-install isn't public
The bundle URL points to your developer sandbox CDN. It's only reachable from your test store; even other developers can't see it.

7. Submit#

When you're happy:
Backend flow:
1. CLI uploads ZIP                           POST /marketplace/themes/{id}/submit
2. Async task: theme_build_task              ↓
   a. unzip
   b. AST scan for forbidden patterns
        - raw eval()
        - document.write()
        - fetch() to non-allowlisted hosts
        - direct DOM injection of <script>
   c. sandboxed Docker re-build from source
        (ignores dev's dist/ — trust nothing)
   d. compare new manifest sha256 vs prior   ← integrity drift
3. Status: building → built → in_review
If any step fails, status → failed with an error message visible in your numu-theme status output + the hub's My-Submissions page.

8. Review#

Admin reviews via the numu-admin backoffice. Inputs:
Visual diff of preview pages vs the prior version
Manifest diff
Build logs
AST scan output
Past submissions for this theme id
Outcomes:
Approved — bundle promoted from sandbox to public R2; marketplace_theme.published_at set
Rejected — message back to the developer with the reason
Pending fixes — you re-submit with a new version
Approval requires admin 2FA (the existing /admin/themes/{id}/approve endpoint enforces it).

9. Publish#

Approved themes are listed on the public marketplace. Merchants discover them via:
Hub → Themes → Browse Marketplace
Direct URL: https://numueg.app/themes/<id>
The bundle URL is now:
https://r2.numueg.app/themes/<id>/<version_id>/theme.js
Cached aggressively (one year Cache-Control); cache-busted by version_id, so a new version → new URL → no stale cache.

10. Merchant install#

A merchant clicks "Install" in the hub:
1. Hub → POST /stores/{store_id}/themes/install   { theme_id, version: "latest" }
2. Backend:
   - Sets store.theme_settings.theme_id = <theme_id>
   - Sets store.theme_settings.external_theme.bundle_url = <R2 url>
   - Resets customizer state to theme.json presets
   - Records install in audit_log
3. Hub redirects to customizer with the new theme loaded
4. Storefront's per-request fetch picks up the new theme_settings on next page load
A customer visiting https://<merchant-sub>.numueg.app/ now sees your bundle render.

Iterating after publish#

The same flow with a new version:
Merchants on ^1.0.0 auto-update on cache expiry. Merchants pinned to 1.0.0 exactly stay on it until they explicitly upgrade.
Marketplace UI shows merchants a "Update available" badge when a new version they're eligible for ships.

Sandbox vs public flow#

StepDeveloper-install (5+6)Public submit (7-10)
Pre-buildUses dev's dist/Re-builds in sandbox
AST scanSoft (lint only)Hard (rejects on forbidden patterns)
Admin reviewNoneRequired
Bundle URLSandbox CDN, privatePublic R2 CDN
Who can installOnly the developerAny merchant
Version countUnlimited (overwrites)Each immutable, versioned
Use developer-install to iterate against real merchant data + payment flows. Use submit only when you'd want a paying customer to see the result.

Troubleshooting#

"Theme has unresolved peer dependencies"#

numu-theme submit does a npm ls and refuses to ship if React/SDK aren't peerDependencies. Fix in package.json.

Build worker stuck "building"#

The worker has a 5-minute watchdog (theme-marketplace-watchdog beat task) that fails orphan builds. Wait ~5 minutes, then re-submit. If it persists, check the build logs in numu-theme status for clues.

"Bundle integrity mismatch"#

The sandboxed re-build produced a theme.js with a different hash than your local. Almost always: env vars or NODE_ENV differences. The sandbox runs npm ci && npm run build with NODE_ENV=production. Match that locally.

"Forbidden global: document.write"#

AST scan caught it. Themes can't inject HTML this way — use <RichText> from the SDK or, if you really need it, talk to the platform team for an exception.

Customer reports "page is broken"#

Steps:
1.
Check numu-theme status for the merchant's installed version
2.
Open the storefront URL with DevTools → check console
3.
Compare against your local numu-theme dev build
4.
Most common: React not externalized → "Hooks must be called inside a function component" errors. See Federation Runtime → Debugging.
Modified atĀ 2026-09-24 13:03:12
Previous
šŸ—‚ļø Themes overview
Next
Glossary
Built with