Keep your elements and CSS classes; only change tag names and add wrappers. The engine targets tag names and ss-* attributes, never class names. This guide uses the plain HTML path; in Astro, Next.js, or React SSR the tags are identical.
1. Wrap the page in a store
Section titled “1. Wrap the page in a store”<body> <storesynk-store domain="your-store.myshopify.com" token="your-public-storefront-token"> <!-- your existing storefront markup, converted below --> </storesynk-store>
<script type="module" src="https://cdn.storesynk.io/elements/0.1/storesynk.js"></script></body>Exactly one store per page. A component outside its provider ancestor renders nothing, silently. See the context model.
2. Wrap each product region in a product
Section titled “2. Wrap each product region in a product”For a single product page, wrap the markup in <storesynk-product handle="…"> (or product-id="…", not both). For a repeating grid, the card template carries no handle; see step 5.
3. Map static elements to tags
Section titled “3. Map static elements to tags”Before:
<div class="product"> <img class="product__img" src="/img/snowboard.jpg" alt="The Classic Snowboard" /> <h1 class="product__title">The Classic Snowboard</h1> <p class="product__price">$699.95</p> <button class="btn btn--primary">Add to cart</button></div>After:
<storesynk-product handle="classic-snowboard" class="product"> <show-image main class="product__img"><img alt="" /></show-image> <h1 class="product__title"><show-title></show-title></h1> <p class="product__price"><show-price></show-price></p> <add-to-cart><button class="btn btn--primary" type="button">Add to cart</button></add-to-cart></storesynk-product>The hardcoded $699.95 is deleted, not kept as a fallback. Money always renders through a displayer so it re-formats on market and currency changes.
| Your static node | Storesynk tag |
|---|---|
| Product title text | <show-title> |
| Price text | <show-price>; strikethrough price → <show-compare-price> (hides itself when not discounted) |
| Vendor / brand line | <show-vendor> |
| Description block | <show-description> (renders the product’s HTML description) |
Main image <img> | keep the <img>, wrap it: <show-image main class="THEIRS"><img alt="" /></show-image>, or <show-media main> for responsive srcset, video, and 3D |
| “Sale” badge | <show-sale-badge format="percentage" class="THEIRS"> (shows only when on sale) |
| A custom field (care instructions, specs) | <show-metafield key="care"> (namespace defaults to custom); wrap label + value in <metafield-wrapper> to hide the block when absent |
Add-to-cart <button> | wrap it: <add-to-cart><button class="THEIRS">Add to cart</button></add-to-cart>; text-out-of-stock / text-unavailable customize the labels |
| “Buy now” button | same pattern with <buy-now> |
| Link to the product page | <product-link> around your <a> (stamps href="/products/{handle}") |
4. Swap option pickers and the quantity stepper
Section titled “4. Swap option pickers and the quantity stepper”Reuse your pill, radio, or select markup as the per-value template inside <change-option>. The first <option-value> (or first <option> in a <select>) is cloned once per value:
<change-option name="Size" class="sizes"> <show-option-label class="sizes__label"></show-option-label> <div class="sizes__values"> <option-value class="sizes__pill"><show-option-title></show-option-title></option-value> </div></change-option>Add <show-option-swatch type="color"> inside the option-value for colour swatches; <show-active-option-title> prints the selected value. name="…" is for a single known product only. On any multi-product route use group="1"/"2"/"3"; a name that matches nothing self-hides and the shopper buys the default variant.
For quantity, wrap your stepper in <change-quantity min="1"> and rename the parts to <decrease-quantity> / <input-quantity> / <increase-quantity>.
5. Repeating markup: keep exactly one template
Section titled “5. Repeating markup: keep exactly one template”Every repeating container keeps its first child as a template, removes it, and clones it per item. Delete your N hard-coded cards down to one:
<storesynk-list collection="best-sellers" limit="8" class="grid"> <!-- ONE card template - cloned per product; note: no handle on the product --> <storesynk-product class="card"> <product-link> <a> <show-image main class="card__img"><img alt="" /></show-image> <span class="card__title"><show-title></show-title></span> </a> </product-link> <show-price class="card__price"></show-price> <add-to-cart><button type="button">Add to cart</button></add-to-cart> </storesynk-product></storesynk-list>The same rule applies to every clone container: search-result-list, predictive-search (an <input> and one item template), cart-line-list, product-list inside <storesynk-collection>, filter-list, change-filter, and active-filter-list. A faceted collection page maps onto <storesynk-collection> the same way.
6. Wire the cart UI
Section titled “6. Wire the cart UI”- Cart icon count →
<show-cart-count>; the open button →<open-cart class="THEIRS">. - The drawer →
<storesynk-cart>with a<cart-line-list>whose first child is one<cart-line>. - Total →
<show-cart-total>; the checkout button →<checkout-link><a class="THEIRS">Checkout</a></checkout-link>.
<storesynk-cart class="drawer"> <div class="drawer__head"> <h2>Your cart</h2> <close-cart aria-label="Close cart">×</close-cart> </div>
<cart-line-list class="drawer__lines"> <cart-line class="line"> <show-line-image class="line__img"><img alt="" /></show-line-image> <div class="line__info"> <show-line-title class="line__title"></show-line-title> <show-line-variant class="line__variant"></show-line-variant> <show-line-price class="line__price"></show-line-price> </div> <div class="line__qty"> <decrease-line-quantity>−</decrease-line-quantity> <show-line-quantity></show-line-quantity> <increase-line-quantity>+</increase-line-quantity> </div> <remove-cart-line class="line__remove">Remove</remove-cart-line> </cart-line> </cart-line-list>
<div class="drawer__foot"> <p class="drawer__total">Total: <show-cart-total></show-cart-total></p> <checkout-link><a class="btn btn--primary">Checkout</a></checkout-link> </div></storesynk-cart>With a <storesynk-cart> on the page, <open-cart> opens the drawer; without one it navigates to /cart (see cart page). For an empty message, style a plain element off storesynk-cart[ss-empty]. Never repurpose <clear-cart>, which wipes the cart on click.
Partials and validation
Section titled “Partials and validation”Ancestor rules judge the rendered tree, not source files. A fragment validated alone reports missing-ancestor errors even though the provider lives in the layout. Don’t add a second <storesynk-store> to fix that; validate the composed result.
Validate each converted page through your connected AI tool before testing in a browser. Fix Errors and Warnings. See validator rules.
Common conversion mistakes
Section titled “Common conversion mistakes”- Leaving N hard-coded cards in a grid. Only the first child is used; the rest is dead markup.
- A displayer outside its provider renders nothing, silently. See nothing renders.
- A second
<storesynk-store>or nested<storesynk-product>. Bindings double up or point at the wrong product. - Hardcoded option
name=on a shared template ships the “wrong item” bug. Usegroup="1"/"2"/"3". - Old tag names.
value-option→option-value,show-label→show-option-title,show-swatch→show-option-swatch,empty-cart→clear-cart. Old names stay inert. - Self-closing custom elements.
<show-title/>is invalid HTML. - Raw money strings. Every price goes through a displayer.
- Credentials injected after the bundle loads. The credential script must run before the bundle
<script>. - Unlabeled wrapped inputs.
apply-discount,cart-note,search-input, andchange-pricewrap your fields; give each anaria-labelor<label>.