Start with the editing model

A theme can be a good fit when the team relies on Shopify’s merchant editing workflow. Compose templates from reusable sections and blocks, and make settings clear enough for day-to-day changes.

Choose between a theme and a custom frontend using editing needs, integrations, design constraints, and maintenance capacity. Revenue alone is not a reliable architecture rule. Neither approach guarantees a particular performance score.

Request and validate the new sections

The Section Rendering API returns rendered sections for a page context. A response can contain a null section even when the HTTP request succeeds, so validate both the status and every requested section.

The request helper below cancels superseded work and rejects stale responses. It uses same-origin URLs. The caller must apply all returned fragments coherently and keep the previous content intact on failure.

CONCEPT / SECTIONS
  1. 01Filter
  2. 02Fetch sections
  3. 03Validate & update
  4. 04URL + announcement
Request sections, validate the response, update the page and history, then announce the result. Retry or navigate normally on failure.
JAVASCRIPT
export function createSectionLoader() {
  let controller;
  let latest = 0;
  return async (pageUrl, sectionIds) => {
    const request = ++latest;
    controller?.abort();
    controller = new AbortController();
    const url = new URL(pageUrl, location.href);
    if (url.origin !== location.origin) throw new Error("Expected same origin");
    url.searchParams.set("sections", sectionIds.join(","));
    const response = await fetch(url, {signal: controller.signal});
    if (!response.ok) throw new Error("Section request failed");
    const sections = await response.json();
    if (request !== latest) return null;
    for (const id of sectionIds) {
      if (typeof sections[id] !== "string") throw new Error("Section unavailable");
    }
    return sections;
  };
}

Request-layer example. A theme must supply its actual section IDs and DOM update behavior.

Finish the interaction, not just the fetch

  1. Mark the results region as busy while loading and keep the selected filters visible.
  2. Validate the response before replacing any region. Ignore cancellation from a newer selection.
  3. Update the URL only after a successful render; handle popstate without pushing a new history entry.
  4. Restore a sensible focus target if its original node was replaced, then announce the result count.
  5. Show a useful no-results state. On failure, retain the existing content and offer retry or normal navigation.

Keep code responsible for fetching separate from DOM replacement and focus handling. Test those boundaries together in the theme, including rapid filtering and browser Back.

Sources & review

Removed unsupported theme-versus-headless speed, GMV, and page-builder comparisons. The example was reviewed against Section Rendering behavior; application DOM wiring and focus policy must be implemented and tested in the actual theme.

Content reviewed 2026-09-08. No production benchmark is claimed.