---
title: Partial tag
description: 'Learn how to update named, server-rendered regions of a theme page.'
source_url:
  html: >-
    https://shopify.dev/docs/storefronts/themes/getting-started/developer-preview/partial
  md: >-
    https://shopify.dev/docs/storefronts/themes/getting-started/developer-preview/partial.md
---

# Partial tag

**Developer preview:**

To use the `{% partial %}` tag and its JavaScript helpers, select **Liquid July '26 changes** from the available [feature previews](https://shopify.dev/docs/api/feature-previews). Help shape this feature while it's in developer preview by sharing what's working and what needs work in the [Shopify developer community](https://community.shopify.dev/).

A partial is a named region of server-rendered HTML that JavaScript can refresh on its own, without a full page reload. You mark the region inline in a template or layout with `{% partial %}`, so there's no separate endpoint or client-side rendering code to maintain.

Until now, updating part of a page without a reload meant pairing the [Section Rendering API](https://shopify.dev/docs/api/ajax/section-rendering) with a client-side framework to stitch the response back in. A partial gives you that behavior from the web platform instead, with three pieces working together:

* **Liquid** renders the region's HTML on the server.
* **JavaScript** fetches fresh HTML for the region and applies it to the page.
* **The partial name** links the response to the region it replaces, so the update lands in the right place.

This builds on the web platform's [Declarative Partial Updates proposal](https://github.com/WICG/declarative-partial-updates). For a walkthrough of the same request-and-replace model, see [Declarative partial updates: a new way to build faster web pages](https://developer.chrome.com/blog/declarative-partial-updates).

***

## Basic syntax

A partial wraps a region of a template or layout in `{% partial %}` and `{% endpartial %}`, and gives it a name. The content inside is regular Liquid that renders with the rest of the page on the first load:

## templates/collection.liquid

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

Wrap only the part that changes, not the whole page. Here, sorting or filtering the collection only needs to refresh the product grid, so the grid is the region worth naming.

The name is how JavaScript targets the region later. When JavaScript requests `product-grid`, the template needs a matching `{% partial 'product-grid' %}` region for the update to apply.

***

## Fetching partials

JavaScript updates a partial in two steps: it requests fresh HTML for the region from the server, then applies that HTML to the page. The `@shopify/partial-rendering` package provides helpers for both steps.

### `fetch()` and `apply()`

`fetch()` requests fresh HTML for one or more named partials from the server and returns the result. `apply()` takes that result and replaces the matching regions on the page.

Calling the two yourself gives you control over the request (its URL, method, and body) and the timing of the update. If you don't need that control, [`refresh()`](#refresh) combines both steps into one call.

The following example updates the `product-grid` region with a new sort order: it builds the request URL from the current page URL, keeping the current locale and existing query parameters, then requests fresh content for the region and applies it:

## assets/collection.js

```js
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);
```

Building the request URL from the current page URL or the [`routes`](https://shopify.dev/docs/api/liquid/objects/routes) object, rather than hardcoding a storefront path, keeps the request working across locales and markets.

One interaction often changes more than one region. Filtering a collection, for example, updates the product grid, the result count, and the active filters. Fetch them together so a single request keeps every region in sync:

## assets/collection.js

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


partials.apply(update);
```

By default, `apply()` swaps in the new content instantly. To animate the change instead, so the old and new content transition smoothly, call `apply()` inside a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API). Fall back to a direct call where the browser doesn't support it:

## assets/collection.js

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

### `refresh()`

Use `refresh()` when the request targets the current page URL, so there's no URL to build. For example, when the browser's back or forward button changes the URL, call `refresh()` to bring the affected regions back in sync with it. It fetches the named partials from the current URL and applies them in one call:

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

To update everything at once after a change that can affect many regions, such as switching currency or language, call `refresh()` with no arguments. Pass an element instead to refresh only the partials inside it:

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

### `get()` and `getAll()`

Sometimes you need to reference a region that's already on the page without fetching or replacing it, such as to measure or read it. `get()` returns the first partial with a given name, and `getAll()` returns every partial in a scope. Each result pairs the partial's name with a [`Range`](https://developer.mozilla.org/en-US/docs/Web/API/Range) spanning its content. For example, read the product grid's current position, or list every region inside the main element:

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


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

***

## Accessibility and state

A partial update replaces content in place, so the browser skips the housekeeping it normally does during a full page load. `apply()` takes care of some of that housekeeping: focus, text selection, form values, and scroll position carry over the swap.

Because there's no full page load, the rest is your code's responsibility. Depending on the interaction, that can include:

* **Stale responses.** A slow earlier request can resolve after a newer one and overwrite it. Passing an `AbortSignal` to `fetch()` lets you cancel in-flight requests when a new one starts, so the latest interaction wins.
* **Loading feedback.** Nothing signals that a request is in flight. Setting `aria-busy` on the region while it updates provides that cue.
* **Announcements.** A silent DOM swap goes unannounced to screen readers. A live region can surface meaningful success and error messages.
* **Transient UI.** State that lives only in the DOM, such as an open disclosure, resets when the region is replaced, so it needs restoring after the update.
* **Server-owned values.** Let the returned markup be the source of truth for values the server owns. After a cart update, for example, quantities come from the response rather than the pre-update DOM. For state that lives in the URL, read from `window.location.search` so shared links and back or forward navigation land on the same result.

***
