Skip to main content

Partials


Partials are named regions of server-rendered HTML that JavaScript can refresh independently, without a full page reload. Mark a region with the partial tag, then use @shopify/partial-rendering to request and apply its updated HTML.

Partials use the web platform's Declarative Partial Updates proposal as a reference. For an introduction to the request-and-replace model, see Declarative partial updates: a new way to build faster web pages.


Wrap only the region that changes, not the whole page. For example, a collection page can name its product grid so sorting or filtering only refreshes that grid:

templates/collection.liquid

{% partial 'product-grid' %}
{% for product in collection.products %}
{% render 'product-card', product: product %}
{% endfor %}
{% endpartial %}

The name links the region to the response that replaces it. A JavaScript request for product-grid needs a matching {% partial 'product-grid' %} in the rendered page.


The @shopify/partial-rendering package provides helpers to fetch, apply, and refresh named partials.

Use fetch() to request fresh HTML for one or more partials. Pass the result to apply() to replace the matching regions on the page. This gives you control over the request URL, method, body, and when the update appears.

This example updates the product grid with a new sort order. It starts with the current URL so the request preserves the current locale and query parameters:

import {partials} from '@shopify/partial-rendering';

const url = new URL(window.location.href);
url.searchParams.set('sort_by', 'price-ascending');

const update = await partials.fetch('product-grid', {
url: url.toString(),
});

partials.apply(update);

Build request URLs from the current page URL or the routes object instead of hardcoding storefront paths. That keeps requests working across locales and markets.

One interaction can change several regions. For example, filtering a collection can update the product grid, result count, and active filters. Fetch them together so one response keeps them in sync:

const update = await partials.fetch(
'product-grid',
'product-count',
'active-filters',
{url: url.toString()},
);

partials.apply(update);

By default, apply() swaps content immediately. To animate the swap, call it inside a View Transition, with a fallback for browsers that don't support transitions:

if (document.startViewTransition) {
document.startViewTransition(() => partials.apply(update));
} else {
partials.apply(update);
}

Use refresh() when the request targets the current page URL. It fetches and applies the named partials in one call, so it works well when the browser's back or forward button changes the URL:

await partials.refresh('product-grid', 'product-count');

Call refresh() with no arguments to update every partial on the page. Pass an element to refresh only the partials inside it:

await partials.refresh();
await partials.refresh(document.querySelector('[data-product-list]'));

Use get() or getAll() to reference partials already on the page without fetching or replacing them. get() returns the first matching partial, and getAll() returns every partial in a scope. Each result pairs the partial name with a Range spanning its content.

const grid = partials.get('product-grid');
const bounds = grid?.range.getBoundingClientRect();

const regions = partials.getAll(document.querySelector('main'));

Anchor to Accessibility and stateAccessibility and state

A partial update replaces content in place, so the browser doesn't perform the housekeeping of a full page load. apply() preserves focus, text selection, form values, and scroll position across the swap.

For the rest, account for the needs of each interaction:

  • Stale responses: A slow earlier request can resolve after a newer one and overwrite it. Pass an AbortSignal to fetch() and cancel in-flight requests when a new interaction starts.
  • Loading feedback: Set aria-busy on a region while it updates.
  • Announcements: Use a live region for meaningful success and error messages that result from a silent DOM swap.
  • Transient UI: Restore DOM-only state, such as an open disclosure, after the region is replaced.
  • Server-owned values: apply() preserves input, textarea, and select values across a swap. If the server changes a form control, such as a cart quantity after validation or an inventory adjustment, update that control after applying the partial instead of expecting the returned markup to replace its value. For URL state, read window.location.search so shared links and browser navigation produce the same result.

Was this page helpful?