OxifyOxify

Troubleshooting

What every failure mode looks like, and why your storefront is never left broken.

A custom template can fail in exactly two ways — at compile time, or at render time — and both are designed to leave your storefront working. (A third case, an error inside a click handler, just loses that click — see below.)

"Compile failed" with a red banner

The switch won't stay on, and the message names the line — counting from one line above your code, so line 2 in the message is the first line you wrote. Usual suspects:

  • A missing closing tag (Unterminated JSX contents)
  • A stray const, if, return, or other statement at the top level
  • More than one top-level element — wrap them in <>…</> (Adjacent JSX elements must be wrapped in an enclosing tag)
  • A semicolon after the closing tag
  • An import line
  • An empty editor — Template is empty.

See The rules for the full list. Nothing is written while a compile is failing, so your live store keeps the last version that compiled.

Looks right in the preview, but the storefront shows the old design

Check the card's master switch is on.

Check the individual template's switch is on.

Check you clicked Save. Compiling alone doesn't reach the storefront.

Check the builder is published. The page header shows Publish while it's still a draft, and draft builders aren't shown to shoppers.

Compiled fine, but the storefront shows the built-in design instead of mine

Your template threw an error while rendering, so the app quietly reverted that piece to the built-in design rather than leave a hole in the page. Open your browser console and look for a message starting with [Oxify Bundle Builder] custom template. It names the piece in quotes — layout, tabButton, productCard, sidebar or toast — and ends with the error.

The usual cause is reading through a value that can be missing — for example props.selectedBox.maxItems when no box is selected. Use props.maxItems instead (always a number), or guard it: props.selectedBox && props.selectedBox.maxItems. Another common one is a style written as a CSS string — style="color: red" — instead of an object.

On a live storefront the built-in design then stays for the rest of that page view. In the admin, fix the code and click Compile again to get another attempt.

The badge says "Edited — compile to apply"

You changed the code since the last compile. The preview and your shoppers still see the older compiled version. Click Compile, then Save.

I reset a template to default, but shoppers still see my old one

Reset to default only replaces the code in the editor. It doesn't turn the template off or touch the version that's already compiled. Either click Compile and Save to publish the starting template, or switch the template off and save to go back to the built-in design.

A button in my template does nothing when clicked

  • Check the spelling: the handler is onClick, not onclick — a lowercase handler is ignored.
  • Errors thrown inside an onClick handler can't be caught the way render errors are, so the click is simply lost. Check the browser console for the error, and make sure the handler only calls functions listed in The five templates — for example onClick={() => props.onAddToKit()}.
  • Pass an id where one is expected: onClick={() => props.onCategoryChange(tab.id)}, not onClick={props.onCategoryChange}.

The add button looks clickable but nothing happens

The app refuses an add when the kit is full, the variant is out of stock, or the category's selection rule is at its limit — and your button gave no sign of it. Pass the disabled state through: disabled={props.isAddButtonDisabled}, which also covers a price that's still loading. When a selection rule is what stopped the add, the app also shows the rule's message in the toast.

My Full layout has no Add to Cart button, or no products

A Full layout only shows what you place. The Add to Cart button lives in the sidebar, so place props.slots.sidebar (or build the panel from props.sidebarProps), and place props.slots.productGrid or props.slots.productCards for the products. See Full layout.

My one-section-per-category layout only shows one category

props.categories holds the products on screen, and the builder starts on the first category when Hide "All Products" Tab is on. Turn that setting off in Tab Button Template. See the sectioned-page recipe in Full layout.

My CSS rules don't apply

  • Open the Custom CSS Settings row (click Add custom CSS if it isn't there) and make sure its switch is on — the CSS only applies while it is.
  • Give your elements the classes your rules target, and keep the template's root class so the built-in styles are still in play. See CSS variables and styling.

Looks right in the preview but wrong on the storefront

The Preview renders inside the admin — a scaled-down desktop view or a phone-width frame — with no theme around it and no live product data: prices and images come from the saved product details, and props.getVariantImage returns null. The storefront has your theme's own CSS, the shopper's real screen width, and live prices, stock and photos.

Check your CSS for fixed widths, and prefer the --oxify-bb-* variables and percentage widths over hard-coded pixels. Also check whether your theme's styles are leaking into elements like buttons, headings or images. @media rules respond to the browser window, not the preview frame, so test those on a real storefront page. See CSS variables and styling.

I want to go back

Turn the template's switch off and save — the built-in design returns as soon as the save goes through, and your code stays in the editor for later. Turn the master switch off to switch every template off at once. Reset to default replaces your code with the starting template.

I'm not sure what a prop contains

Print it. Drop a line like this into your markup while you work — it's safe for plain data such as props.product, props.boxes or props.tabs:

<pre>{JSON.stringify(props.boxes, null, 2)}</pre>

Don't print props.slots, a card, or props.subscriptionBlock — those are rendered elements, not plain data. Remove the line before you publish.


FAQ

Do I need to customize all five templates? No. Each is independent; anything you leave off keeps the built-in design.

Will my template break my store if I make a mistake? Errors can't. A template that doesn't compile can't be switched on, and one that fails while rendering is replaced by the built-in design for the rest of that page view. What a template can't protect you from is leaving things out on purpose — a layout that never places the sidebar has no Add to Cart button.

Can I add my own analytics or tracking to a button? You don't need to. The builder records its own analytics, and props.onAddToCart is what adds the bundle with the properties that orders and analytics rely on. Use the handlers in props rather than making your own cart calls, or the bundle won't be recognized.

Can shoppers see my code? Only the compiled version goes to the storefront — your original JSX stays in the admin — but anyone can read what reaches their browser. Keep anything private out of a template.

Can I copy a template between bundle builders? Yes — copy the code out of the editor and paste it into the other builder's editor, then compile and save there.

Can support help me write or debug my template? No — this is a developer feature. Support can help with everything else in the app, but writing and debugging custom JSX is outside what they cover.