OxifyOxify

Full layout

Rearrange the whole builder — decide where the tabs, grid, and sidebar sit instead of using the built-in arrangement.

Full layout is the one custom template that's different in kind from the other four. They change what a piece looks like; Full layout changes where the pieces go.

Reach for it when the built-in arrangement is the problem — you want the sidebar above the grid instead of beside it, or a section per category, or your own grid container around the cards.

What you get

Every piece of the builder arrives ready-made on props.slots, already wired to data, pricing, and the cart. You place them; the app keeps running them.

<div className="oxify-bb-layout-root">
  {props.slots.tabs}
  {props.slots.categoryTitle}
 
  <div className="oxify-bundle-builder-layout">
    <div className="oxify-bb-products-section">
      {props.slots.productGrid}
    </div>
 
    {props.slots.sidebar}
  </div>
</div>

That's the starting template the editor gives you — it reproduces the built-in arrangement exactly, so you edit working markup rather than starting from a blank box. (oxify-bb-layout-root is just a plain wrapper with no styles of its own — call it whatever you like.)

The Full layout tab selected in the Custom design template card, showing the default JSX with props.slots.tabs, productGrid and sidebar

Note the help text under the editor: it repeats the wrapper rule below, so you have it in front of you while you write.

The one class that controls everything

This is the mechanic that makes Full layout click, and it's worth reading twice:

Where the sidebar sitsWhat it does
Inside an oxify-bundle-builder-layout wrapperThe built-in side-by-side row, with a sticky sidebar column that follows the shopper down the page — 320px wide by default, or whatever Sidebar Width is set to in Sidebar Widget.
Outside that wrapperThe sidebar automatically becomes a normal full-width, non-sticky block — ready to sit above or below the grid.

So moving the kit panel from beside the grid to above it isn't a CSS job. You just move the element out of that wrapper, and the sidebar reshapes itself. Inside the wrapper, keep your products in an oxify-bb-products-section so they fill the space beside the sidebar.

On screens 1024px wide or narrower the row stacks and the sidebar becomes a full-width, non-sticky block even inside the wrapper.

Your Design-tab settings keep working

Your markup renders inside oxify-bundle-builder-container, so all the color, spacing, and typography settings still apply — and so does your Custom CSS.

The slots

SlotWhat it is
props.slots.tabsThe category switcher. null when the merchant turned the Tab Button Template row off.
props.slots.categoryTitleThe heading above the products. null when the Category Title switch (inside Tab Button Template) is off.
props.slots.productGridThe full grid, including loading placeholders and the "no products" message.
props.slots.productCardsThe same cards as a bare array, for when you want your own grid element around them.
props.slots.sidebarThe kit panel — your Sidebar template if that's active. Includes the Buy Box (Sidebar Widget → Buy Box) when that template is selected; the Buy Box only appears once the kit has an item.

Guard the nullable slots

tabs and categoryTitle are null when the merchant switched those settings off. Placing a null is harmless — but don't wrap one in your own decorative container, or you'll render an empty box.

Place the sidebar

The Add to Cart button lives in the sidebar. A layout that doesn't place props.slots.sidebar — or build the panel yourself from props.sidebarProps — leaves shoppers with no way to add the bundle to the cart. The same goes for the product grid or cards: leave them out and the shopper sees no products.

The other props

PropWhat it is
props.productsThe products on screen, in grid order, as { product, categoryId, categoryName, isFirstInCategory, card } — each with its rendered card. Empty while isLoadingProducts is true.
props.categoriesThe same products regrouped per category, as { id, name, cards, grid } — each with rendered cards and a ready-made grid element that carries the same columns and styling as the main grid. On the All tab every category appears; on a single tab, just that one.
props.tabsPropsThe Category tabs props bag — exactly what that template receives, for writing the switcher inline instead of placing props.slots.tabs.
props.sidebarPropsThe Sidebar props bag, for writing the panel inline instead of placing props.slots.sidebar. It includes the Subscribe & Save props.
props.activeCategoryId / props.onCategoryChangeThe selected category (or "all"), and a function to switch it.
props.categoryTitleDisplay name of the active category, as a plain string (All Products on the All tab).
props.isLoadingProductsTrue while availability is still resolving and the grid is holding placeholders — only when the merchant hides out-of-stock products. Don't paint prices or counts off products yet.
props.hasProductsFalse when the active category has nothing to show — and also while isLoadingProducts is true, so check that first.
props.currencyFormatThe store's money format string.
props.formatPriceFormat any number in the store's currency.
props.isAdminPreviewTrue in the admin Preview panel, false on a live storefront.

Recipe — sidebar above the products, full width

The kit panel is the thing shoppers watch fill up, so it often belongs at the top. No new CSS needed: outside the oxify-bundle-builder-layout wrapper, the sidebar becomes a full-width block on its own.

<div className="oxify-bb-layout-root">
  {props.slots.tabs}
 
  {props.slots.sidebar}
 
  {props.slots.categoryTitle}
  {props.slots.productGrid}
</div>

Move the sidebar line after productGrid for the bottom variant.

Recipe — your own grid around our cards

Keeps every card's behavior — variants, live pricing, stock, add and remove — while you own the container it sits in.

<div className="oxify-bb-layout-root">
  {props.slots.tabs}
 
  <div className="my-masonry">
    {props.slots.productCards}
  </div>
 
  {props.slots.sidebar}
</div>

Use props.slots.productCards rather than props.slots.productGrid — the latter brings our grid element with it. The cards are already keyed, so render the array as-is.

Recipe — wrap each card yourself

When you need a wrapper around every card — or a heading before the first card of each category — map over props.products. Each entry's card is the same rendered card, so variants, pricing and stock still work inside it.

<div className="oxify-bb-layout-root">
  <div className="my-grid">
    {props.products.map((entry) => (
      <div key={entry.card.key} className="my-cell">
        {entry.isFirstInCategory && <h4>{entry.categoryName}</h4>}
        {entry.card}
      </div>
    ))}
  </div>
 
  {props.slots.sidebar}
</div>

Recipe — one section per category

The sectioned page: every category under its own heading, each with a real products grid. category.grid carries the same columns and styling as the main one, so there's no grid CSS to write.

<div className="oxify-bb-layout-root">
  {props.categories.map((category) => (
    <React.Fragment key={category.id}>
      {category.name && (
        <h3 className="my-category-heading">{category.name}</h3>
      )}
      {category.grid}
    </React.Fragment>
  ))}
 
  {props.slots.sidebar}
</div>

Two things to know alongside it. Leave Hide "All Products" Tab off in Tab Button Template — with it on, the builder starts on the first category and props.categories holds only that one. And this template doesn't place props.slots.tabs, so there's no tab bar for shoppers to switch with — the sections replace it.

What Full layout doesn't change

The toastAlways rendered outside the layout, so a layout that never places it still shows add-to-bundle confirmations (as long as Add to Cart Message is on). It has its own template for markup changes.
What each piece looks likeThe Product card, Category tabs, and Sidebar templates still decide that. Full layout only decides where they go — and it picks up those templates automatically when they're active.
Per-product behaviorVariant selection, live pricing, and stock stay inside the card. You get the rendered element, not a copy of its state.
What happens on an errorIf your layout throws while rendering, the app shows the built-in arrangement instead, for the rest of that page view.

Combine it with the other templates

Full layout and the other four are independent. A common setup is Full layout to move the sidebar up top, plus Product card to restyle the cards — each doing the job it's built for.

On this page