Magento
Product Shell Bundle
A headless storefront that calls Magento GraphQL for every product page is slow and load-sensitive. Product Shell Bundle solves that by pre-building a “shell” for each product — all the slowly-changing data (name, description, media, attributes, variants, customizable options, categories, related products, reviews) plus indexed price and stock — into a paginated JSON snapshot and a matching GraphQL feed. Your storefront renders the page instantly from the shell, then overlays live price and stock for one product on hydration.

Compatibility
Section titled “Compatibility”PHP
Delivery
Extensible
What’s in a shell
Section titled “What’s in a shell”Each product shell carries everything a PDP needs to render without a second call:
| Group | Includes |
|---|---|
| Core | sku, name, url_key, type, descriptions, meta fields |
| Media | base/small/thumbnail/swatch images + full gallery |
| Commerce | indexed price_range and stock_status (anonymous group) |
| Variants | configurable options + per-variant price/stock |
| Options | customizable options (drop-down, radio, field…) with prices |
| Relations | category IDs, related/upsell/cross-sell SKUs |
| Social proof | rating summary, review count, recent approved reviews |
| Type extras | bundle / grouped / downloadable specifics |
Two ways to consume it
Section titled “Two ways to consume it”{ productShellBundle(page_size: 100, current_page: 1) { total_count total_pages current_page products { sku name type_id stock_status price_range { minimum_price { final_price { value currency } } } } categories { category_id name url_path } }}Paginated (page size capped for safety) so you can warm your storefront cache page by page.
{ productShellSingle(sku: "24-MB01") { sku name stock_status price_range { minimum_price { final_price { value currency } } } } }A surgical single-product refresh for webhook handlers — re-fetch just the product that changed instead of rebuilding the whole bundle (~150 ms vs several seconds).
From Sync Products → Shell Bundle, click Refresh Bundle to write a snapshot to
var/product_shell_bundle/, then Download JSON (or pull it from your build pipeline). A
version hash on every bundle lets you skip rebuilds when nothing changed.
Correct & safe
Section titled “Correct & safe”Only enabled products
Both the bundle and the single-product feed return enabled products only — a disabled product never leaks into the snapshot or via a guessed SKU.
Anonymous-accurate pricing
Price and stock are read from Magento’s price index and MSI stock view for the NOT LOGGED IN group, so the shell matches what an anonymous shopper sees — your storefront overlays customer-specific pricing live.
Approved reviews only
Embedded reviews are the most recent approved ones, store-scoped — ready for the reviews block and aggregateRating JSON-LD.
Safe downloads
The admin JSON download is permission-gated and the store code is sanitised, so the snapshot path can’t be manipulated.
Why a shell plus live overlay, instead of just calling GraphQL?
Most product data barely changes between catalogue edits, so serving it from a pre-warmed snapshot makes PDPs render instantly. Only the volatile bits — price and stock — are fetched live per product on hydration, which keeps pages both fast and accurate.
Will disabled products show up in the feed?
No — the bundle and the productShellSingle query both filter to enabled products, so a disabled
product can’t appear in the snapshot or be pulled by guessing its SKU.
How do I refresh just one product after an edit?
Call productShellSingle(sku: …) (or your webhook handler does) to re-fetch that single shell in
about 150 ms, rather than rebuilding the whole bundle.
Can other modules add data to each product shell?
Yes — a module can register a shell contributor that adds a keyed entry to each product’s
extensions map, without modifying this module. A misbehaving contributor is logged and skipped, so
it can never break the build. Verified clean on PHP 8.4 and 8.5.