---
title: "Use Shopify with Prismic"
description: "Learn how to build websites with Shopify and Prismic."
category: "integrations"
audience: developers
lastUpdated: "2025-11-06T01:07:50.000Z"
---

This guide shows how to integrate Shopify with Prismic to display products and collections in your content.

You'll learn two approaches:

1. Using Prismic's built-in Shopify integration to select individual products.
2. Creating a custom integration catalog using an API endpoint.

Both methods allow content writers to choose Shopify data directly from the Prismic editor while giving developers full control over how the data is displayed.

# Prerequisites

Before you begin, you'll need to set up Shopify API access for your store.

1. **Create a Shopify custom app**

   Shopify requires custom apps for API access. Navigate to your Shopify admin and go to **Settings** > **Apps and sales channels** > **Develop apps** and click **Create an app**.

   Use the following values when creating your app:

   | Field                            | Value                                          |
   | -------------------------------- | ---------------------------------------------- |
   | **App Name**                     | "Prismic Integration" (or your preferred name) |
   | **Admin API access scopes**      | Products: Read access                          |
   | **Storefront API access scopes** | Products: Read access                          |

   Click **Create app** and then **Install app** to finish setup.

   [Learn more about Shopify custom apps](https://help.shopify.com/en/manual/apps/app-types/custom-apps)

2. **Set up environment variables**

   After creating your app, save your Storefront API credentials as environment variables in a `.env` file:

   ```bash filename=.env
   # Your Shopify store domain (without https://)
   SHOPIFY_STORE_DOMAIN=your-store-name.myshopify.com

   # Your Shopify Storefront API access token
   SHOPIFY_STOREFRONT_ACCESS_TOKEN=your_storefront_token_here
   ```

   > You'll also need your Admin API credentials (API key, access token, and shared secret) to use Prismic's built-in Shopify product integration. You do not need to add them to your `.env` file.

# Select products in Prismic

Follow these steps when content writers need to select an individual product from a Shopify store.

> This section uses Prismic's built-in Shopify integration with the Admin API credentials from the [prerequisites](#prerequisites) section.

1. **Create a Shopify integration catalog**

   Prismic has a native integration to select products from a Shopify using an [integration](https://prismic.io/docs/fields/integration.md) field.

   To begin, follow the linked guide to create a Shopify integration field catalog.

   [Learn how to create a Shopify integration catalog](https://prismic.io/docs/fields/integration.md#sync-products-from-a-shopify-store)

2. **Add a product field to a content model**

   After creating the catalog, connect it to an [integration](https://prismic.io/docs/fields/integration.md) field in a [slice](https://prismic.io/docs/slice.md), [page type](https://prismic.io/docs/content-modeling.md#page-types), or [custom type](https://prismic.io/docs/content-modeling.md#custom-types) depending on where you need the product data.

   [Learn how to add an integration field](https://prismic.io/docs/fields/integration.md#add-an-integration-field-to-a-content-model)

3. **Display the product**

   The integration field provides basic [product metadata](#product-metadata). In some cases, content from the metadata is sufficient.

   **Next.js example:**

   ```tsx
   import { isFilled } from "@prismicio/client";

   {
     isFilled.integration(slice.primary.product) && (
       <div>
         <h3>{slice.primary.product.title}</h3>
         <p>{slice.primary.product.vendor}</p>
         <div
           dangerouslySetInnerHTML={{ __html: slice.primary.product.body_html }}
         />
       </div>
     );
   }
   ```

   **Nuxt example:**

   ```vue-html
   <script setup>
   import { isFilled } from "@prismicio/client";
   </script>

   <div v-if="isFilled.integration(slice.primary.product)">
     <h3>{{ slice.primary.product.title }}</h3>
     <p>{{ slice.primary.product.vendor }}</p>
     <div v-html="slice.primary.product.body_html" />
   </div>
   ```

   **SvelteKit example:**

   ```svelte
   <script>
     import { isFilled } from "@prismicio/client";
   </script>

   {#if isFilled.integration(slice.primary.product)}
     <div>
       <h3>{slice.primary.product.title}</h3>
       <p>{slice.primary.product.vendor}</p>
       <div>{@html slice.primary.product.body_html}</div>
     </div>
   {/if}
   ```

   If you need additional product data, use Shopify's [Storefront API client](https://github.com/Shopify/shopify-app-js/tree/main/packages/api-clients/storefront-api-client).

   ```sh
   npm install @shopify/storefront-api-client
   ```

   This example shows how to fetch a product's `title` and `isGiftCard` properties using the Shopify Storefront API client.

   **Next.js example:**

   ```tsx filename=src/slices/FeaturedProduct/index.tsx collapsed
   import type { Content } from "@prismicio/client";
   import { isFilled } from "@prismicio/client";
   import type { SliceComponentProps } from "@prismicio/react";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import type { ShopifyCollection } from "@/app/api/collections/route";

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${process.env.SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!,
   });

   type FeaturedProductProps = SliceComponentProps<Content.FeaturedProductSlice>;

   // Using the App Router and a React Server Component, this component can fetch
   // data at render time on the server.
   export default async function FeaturedProduct({ slice }: FeaturedProductProps) {
     if (!isFilled.integration(slice.primary.product)) {
       return null;
     }

     // Fetch additional product data directly in the component.
     const product = await shopify.request(
       `#graphql
         query ($handle: String!) {
           product(handle: $handle) {
             title
             isGiftCard
           }
         }
       `,
       {
         variables: {
           handle: slice.primary.product.handle,
         },
       },
     );

     return (
       <div>
         <h3>{product.data?.product?.title}</h3>
         <p>
           {product.data?.product?.isGiftCard
             ? "This is a gift card."
             : "This is not a gift card."}
         </p>
       </div>
     );
   }
   ```

   **Nuxt example:**

   ```vue filename=~/slices/FeaturedProduct/index.vue collapsed
   <script setup lang="ts">
   import type { Content } from "@prismicio/client";
   import { isFilled } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";

   const props = defineProps(
     getSliceComponentProps<Content.FeaturedProductSlice>(["slice"]),
   );

   const shopify = createStorefrontApiClient({
     storeDomain: "https://your-shop-name.myshopify.com",
     apiVersion: "2023-10",
     publicAccessToken: "your-storefront-public-access-token",
   });

   // useAsyncData provides universal data fetching (SSR + client-side navigation)
   const { data: product } = await useAsyncData(
     `product-${props.slice.primary.product.handle}`,
     async () => {
       if (!isFilled.integration(props.slice.primary.product)) {
         return null;
       }

       // This runs on the server during SSR and on the client during navigation
       const response = await shopify.request(
         `#graphql
           query ($handle: String!) {
             product(handle: $handle) {
               title
               isGiftCard
             }
           }
         `,
         {
           variables: {
             handle: props.slice.primary.product.handle,
           },
         },
       );
       return response.data?.product;
     },
   );
   </script>

   <template>
     <div v-if="product">
       <h3>{{ product.title }}</h3>
       <p>
         {{
           product.isGiftCard ? "This is a gift card." : "This is not a gift card."
         }}
       </p>
     </div>
   </template>
   ```

   **SvelteKit example:**

   ```ts filename=src/routes/[[preview=preview]]/[uid]/+page.server.ts collapsed
   // SvelteKit server-side data fetching strategy:
   // - All external API calls happen on the server in +page.server.ts
   // - Use mapSliceZone to enhance slices with additional data
   // - Components receive pre-fetched data as props (no client-side fetching)

   import type { PageServerLoad } from "./$types";
   import { createClient } from "$lib/prismicio";
   import { mapSliceZone, isFilled } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import {
     SHOPIFY_STORE_DOMAIN,
     SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   } from "$env/static/private";

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   });

   export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
     const client = createClient({ fetch, cookies });
     const page = await client.getByUID("page", params.uid);

     // mapSliceZone processes each slice type and allows data enhancement
     // This pattern scales well - add more slice types as needed
     const slices = await mapSliceZone(page.data.slices, {
       featured_product: async ({ slice, ...otherProps }) => {
         if (!isFilled.integration(slice.primary.product)) {
           return {
             slice,
             ...otherProps,
             product: null,
           };
         }

         // Fetch product data for this specific slice
         const product = await shopify.request(
           `#graphql
             query ($handle: String!) {
               product(handle: $handle) {
                 title
                 isGiftCard
               }
             }
           `,
           {
             variables: {
               handle: slice.primary.product.handle,
             },
           },
         );

         // Return the slice with additional data attached
         // This data will be passed as props to the slice component
         return {
           slice,
           ...otherProps,
           product: product.data?.product || null,
         };
       },
       // Add more slice handlers here as needed
       // e.g., product_grid: async ({ slice }) => { ... }
     });

     return { page, slices };
   };
   ```

   Render the page's slices using the `slices` property, which contains the updated slice data.

   ```svelte filename=src/routes/[[preview=preview]]/[uid]/+page.svelte
   <script lang="ts">
     import { SliceZone } from "@prismicio/svelte";
     import { components } from "$lib/slices";

     let { data } = $props();
   </script>

   <SliceZone slices={data.slices} {components} />
   ```

   Use the `product` prop added in `+page.server.ts`:

   ```svelte filename=src/lib/slices/FeaturedProduct/index.svelte
   <script lang="ts">
     import type { Content } from "@prismicio/client";
     import type { SliceComponentProps } from "@prismicio/svelte";

     type Props = SliceComponentProps<Content.FeaturedProductSlice> & {
       product: {
         title: string;
         isGiftCard: boolean;
       };
     };

     let { slice, product }: Props = $props();
   </script>

   <div>
     <h3>{product.title}</h3>
     <p>
       {product.isGiftCard ? "This is a gift card." : "This is not a gift card."}
     </p>
   </div>
   ```

   You can type your Shopify GraphQL queries with [GraphQL Codegen](https://github.com/Shopify/shopify-app-js/tree/main/packages/api-clients/storefront-api-client#typing-variables-and-return-objects).

4. **Add a product to a page**

   Test your new field by selecting a product in a page. If everything was set up correctly, you should see the product displayed on your page.

## Product metadata

The product metadata matches what is provided by Shopify's [REST Admin API](https://shopify.dev/docs/api/admin-rest).

[See the Product resource reference](https://shopify.dev/docs/api/admin-rest/2025-07/resources/product#resource-object)

Here is an example of what an integration field containing a Shopify product looks like from the Content API:

```json collapsed
{
  "id": 7234567890123,
  "title": "Classic Leather Wallet",
  "body_html": "<p>Handcrafted from premium full-grain leather, this classic bifold wallet combines timeless style with modern functionality. Features 8 card slots, 2 bill compartments, and RFID protection.</p>",
  "vendor": "Premium Leather Co.",
  "product_type": "Accessories",
  "created_at": "2024-01-15T10:30:00-05:00",
  "handle": "classic-leather-wallet",
  "updated_at": "2024-11-20T14:45:30-05:00",
  "published_at": "2024-01-15T10:35:00-05:00",
  "template_suffix": null,
  "published_scope": "web",
  "tags": "wallet, leather, accessories, gifts, mens",
  "status": "active",
  "admin_graphql_api_id": "gid://shopify/Product/7234567890123",
  "variants": [
    {
      "id": 42345678901234,
      "product_id": 7234567890123,
      "title": "Brown",
      "price": "49.99",
      "position": 1,
      "inventory_policy": "deny",
      "compare_at_price": "69.99",
      "option1": "Brown",
      "option2": null,
      "option3": null,
      "created_at": "2024-01-15T10:30:00-05:00",
      "updated_at": "2024-11-20T14:45:30-05:00",
      "taxable": true,
      "barcode": "736594012845",
      "fulfillment_service": "manual",
      "grams": 85,
      "inventory_management": "shopify",
      "requires_shipping": true,
      "sku": "CLW-001-BRN",
      "weight": 0.187,
      "weight_unit": "lb",
      "inventory_item_id": 44567890123456,
      "inventory_quantity": 42,
      "old_inventory_quantity": 45,
      "admin_graphql_api_id": "gid://shopify/ProductVariant/42345678901234",
      "image_id": 31234567890123
    }
  ],
  "options": [
    {
      "id": 9123456789012,
      "product_id": 7234567890123,
      "name": "Color",
      "position": 1,
      "values": ["Brown", "Black", "Tan"]
    }
  ],
  "images": [
    {
      "id": 31234567890123,
      "alt": "Brown leather wallet front view",
      "position": 1,
      "product_id": 7234567890123,
      "created_at": "2024-01-15T10:32:00-05:00",
      "updated_at": "2024-01-15T10:32:00-05:00",
      "admin_graphql_api_id": "gid://shopify/ProductImage/31234567890123",
      "width": 2048,
      "height": 2048,
      "src": "https://cdn.shopify.com/s/files/1/0612/3456/7890/products/classic-leather-wallet-brown.jpg?v=1705331520",
      "variant_ids": [42345678901234]
    }
  ],
  "image": {
    "id": 31234567890123,
    "alt": "Brown leather wallet front view",
    "position": 1,
    "product_id": 7234567890123,
    "created_at": "2024-01-15T10:32:00-05:00",
    "updated_at": "2024-01-15T10:32:00-05:00",
    "admin_graphql_api_id": "gid://shopify/ProductImage/31234567890123",
    "width": 2048,
    "height": 2048,
    "src": "https://cdn.shopify.com/s/files/1/0612/3456/7890/products/classic-leather-wallet-brown.jpg?v=1705331520",
    "variant_ids": [42345678901234]
  }
}
```

# Select product collections in Prismic

Follow these steps when content writers need to select an individual product collection from a Shopify store. The collection's products can be displayed from the website.

> This section uses the Storefront API credentials you set up in the [prerequisites](#prerequisites) section.

1. **Add an integration endpoint to your website**

   Making product collections available to [integration](https://prismic.io/docs/fields/integration.md) fields requires a custom integration catalog.

   To begin, install Shopify's [Storefront API client](https://github.com/Shopify/shopify-app-js/tree/main/packages/api-clients/storefront-api-client).

   ```sh
   npm install @shopify/storefront-api-client
   ```

   Next, add an API endpoint that returns your store's collections.

   **Next.js example:**

   ```ts filename=src/app/api/collections/route.ts collapsed
   import type {
     IntegrationAPIItem,
     IntegrationAPIResults,
   } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";

   export const dynamic = "force-dynamic";

   const MAX_PER_PAGE = 50;

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${process.env.SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!,
   });

   export type ShopifyCollection = {
     id: string;
     handle: string;
     title: string;
     description: string;
     image?: {
       url: string;
     };
   };

   export async function GET(request: Request) {
     const { searchParams } = new URL(request.url);
     const page = parseInt(searchParams.get("page") ?? "1") || 1;

     const response = await shopify.request(
       `#graphql
         query {
           collections(first: 250) {
             edges {
               node {
                 id
                 title
                 description
                 handle
                 updatedAt
                 image {
                   url
                 }
               }
             }
           }
         }
       `,
     );

     const collections = response.data?.collections?.edges ?? [];
     const startIndex = (page - 1) * MAX_PER_PAGE;
     const endIndex = startIndex + MAX_PER_PAGE;
     const paginatedCollections = collections.slice(startIndex, endIndex);

     const results: IntegrationAPIItem<ShopifyCollection>[] =
       paginatedCollections.map(({ node }) => ({
         id: node.id,
         title: node.title,
         description: node.description,
         image_url: node.image?.url,
         last_update: new Date(node.updatedAt).getTime(),
         blob: {
           id: node.id,
           handle: node.handle,
           title: node.title,
           description: node.description,
           image: node.image,
         },
       }));

     const response_data: IntegrationAPIResults<ShopifyCollection> = {
       results_size: collections.length,
       results,
     };

     return Response.json(response_data);
   }
   ```

   **Nuxt example:**

   ```ts filename=~/server/api/collections.ts collapsed
   import type {
     IntegrationAPIItem,
     IntegrationAPIResults,
   } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";

   const MAX_PER_PAGE = 50;

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${process.env.SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!,
   });

   export type ShopifyCollection = {
     id: string;
     handle: string;
     title: string;
     description: string;
     image?: {
       url: string;
     };
   };

   export default defineEventHandler(async (event) => {
     const query = getQuery(event);
     const page = parseInt((query.page as string) ?? "1") || 1;

     const response = await shopify.request(
       `#graphql
         query {
           collections(first: 250) {
             edges {
               node {
                 id
                 title
                 description
                 handle
                 updatedAt
                 image {
                   url
                 }
               }
             }
           }
         }
       `,
     );

     const collections = response.data?.collections?.edges ?? [];
     const startIndex = (page - 1) * MAX_PER_PAGE;
     const endIndex = startIndex + MAX_PER_PAGE;
     const paginatedCollections = collections.slice(startIndex, endIndex);

     const results: IntegrationAPIItem<ShopifyCollection>[] =
       paginatedCollections.map(({ node }) => ({
         id: node.id,
         title: node.title,
         description: node.description,
         image_url: node.image?.url,
         last_update: new Date(node.updatedAt).getTime(),
         blob: {
           id: node.id,
           handle: node.handle,
           title: node.title,
           description: node.description,
           image: node.image,
         },
       }));

     const response_data: IntegrationAPIResults<ShopifyCollection> = {
       results_size: collections.length,
       results,
     };

     return response_data;
   });
   ```

   **SvelteKit example:**

   ```ts filename=src/routes/api/collections/+server.ts collapsed
   import type {
     IntegrationAPIItem,
     IntegrationAPIResults,
   } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import {
     SHOPIFY_STORE_DOMAIN,
     SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   } from "$env/static/private";
   import type { RequestHandler } from "./$types";

   const MAX_PER_PAGE = 50;

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   });

   export type ShopifyCollection = {
     id: string;
     handle: string;
     title: string;
     description: string;
     image?: {
       url: string;
     };
   };

   export const GET: RequestHandler = async ({ url }) => {
     const page = parseInt(url.searchParams.get("page") ?? "1") || 1;

     const response = await shopify.request(
       `#graphql
         query {
           collections(first: 250) {
             edges {
               node {
                 id
                 title
                 description
                 handle
                 updatedAt
                 image {
                   url
                 }
               }
             }
           }
         }
       `,
     );

     const collections = response.data?.collections?.edges ?? [];
     const startIndex = (page - 1) * MAX_PER_PAGE;
     const endIndex = startIndex + MAX_PER_PAGE;
     const paginatedCollections = collections.slice(startIndex, endIndex);

     const results: IntegrationAPIItem<ShopifyCollection>[] =
       paginatedCollections.map(({ node }) => ({
         id: node.id,
         title: node.title,
         description: node.description,
         image_url: node.image?.url,
         last_update: new Date(node.updatedAt).getTime(),
         blob: {
           id: node.id,
           handle: node.handle,
           title: node.title,
           description: node.description,
           image: node.image,
         },
       }));

     const response_data: IntegrationAPIResults<ShopifyCollection> = {
       results_size: collections.length,
       results,
     };

     return Response.json(response_data);
   };
   ```

   > This endpoint fetches up to 250 collections from your Shopify store. Larger catalogs need a custom implementation to fetch all collections.

2. **Deploy your website**

   Your API endpoint needs to be deployed and accessible by Prismic.

   Follow the deployment instructions for [Next.js](https://prismic.io/docs/nextjs.md#deploy), [Nuxt](https://prismic.io/docs/nuxt.md#deploy), or [SvelteKit](https://prismic.io/docs/sveltekit.md#deploy) before continuing. Remember to add your environment variables to your deployment.

   You'll use your website's deployed URL in the next step.

3. **Create an integration catalog**

   Follow the linked guide to connect the API endpoint to a custom integration catalog.

   [Learn how to create a custom integration catalog](https://prismic.io/docs/fields/integration.md#open-your-repository-settings)

   Use the following field values when creating the catalog:

   | Field        | Description                                                                                                                            |
   | ------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
   | Catalog Name | "Product Collections"                                                                                                                  |
   | Description  | "Product collections from the Shopify store."                                                                                          |
   | Endpoint     | The full public URL to the API endpoint (e.g. `https://example.com/api/collections`)                                                   |
   | Access Token | An [optional secret](https://prismic.io/docs/fields/integration.md#create-a-custom-api-catalog) string used to authenticate API calls. |

4. **Add a collection field to a content model**

   After creating the catalog, connect it to an [integration](https://prismic.io/docs/fields/integration.md) field in a [slice](https://prismic.io/docs/slice.md), [page type](https://prismic.io/docs/content-modeling.md#page-types), or [custom type](https://prismic.io/docs/content-modeling.md#custom-types) depending on where you need the product data.

   [Learn how to add an integration field](https://prismic.io/docs/fields/integration.md#add-an-integration-field-to-a-content-model)

5. **Display the collection**

   The integration field provides [collection metadata](#product-collection-metadata). In some cases, content from the metadata is sufficient.

   **Next.js example:**

   ```tsx
   import { isFilled } from "@prismicio/client";

   {
     isFilled.integration(slice.primary.collection) && (
       <div>
         <h3>{slice.primary.collection.title}</h3>
         <p>{slice.primary.collection.description}</p>
       </div>
     );
   }
   ```

   **Nuxt example:**

   ```vue-html
   <script setup>
   import { isFilled } from "@prismicio/client";
   </script>

   <div v-if="isFilled.integration(slice.primary.collection)">
     <h3>{{ slice.primary.collection.title }}</h3>
     <p>{{ slice.primary.collection.description }}</p>
   </div>
   ```

   **SvelteKit example:**

   ```svelte
   <script>
     import { isFilled } from "@prismicio/client";
   </script>

   {#if isFilled.integration(slice.primary.collection)}
     <div>
       <h3>{slice.primary.collection.title}</h3>
       <p>{slice.primary.collection.description}</p>
     </div>
   {/if}
   ```

   If you need to fetch the collection's products, use Shopify's [Storefront API client](https://github.com/Shopify/shopify-app-js/tree/main/packages/api-clients/storefront-api-client).

   This example shows how to fetch a collection's products using the Shopify Storefront API client.

   **Next.js example:**

   ```tsx filename=src/slices/FeaturedCollection/index.tsx collapsed
   import type { Content } from "@prismicio/client";
   import { isFilled } from "@prismicio/client";
   import type { SliceComponentProps } from "@prismicio/react";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import type { ShopifyCollection } from "@/app/api/collections/route";

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${process.env.SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!,
   });

   type FeaturedCollectionProps =
     SliceComponentProps<Content.FeaturedCollectionSlice>;

   // Using the App Router and a React Server Component, this component can fetch
   // data at render time on the server.
   export default async function FeaturedCollection({
     slice,
   }: FeaturedCollectionProps) {
     if (!isFilled.integration(slice.primary.collection)) {
       return null;
     }

     const collection = slice.primary.collection as ShopifyCollection;

     // Fetch collection with products directly in the component.
     const response = await shopify.request(
       `#graphql
         query ($handle: String!) {
           collection(handle: $handle) {
             title
             description
             products(first: 10) {
               edges {
                 node {
                   id
                   title
                   handle
                   priceRange {
                     minVariantPrice {
                       amount
                       currencyCode
                     }
                   }
                 }
               }
             }
           }
         }
       `,
       {
         variables: {
           handle: collection.handle,
         },
       },
     );

     const collection = response.data?.collection;

     return (
       <div>
         <h2>{collection?.title}</h2>
         <p>{collection?.description}</p>
         <div>
           {collection?.products.edges.map(({ node: product }) => (
             <div key={product.id}>
               <h3>{product.title}</h3>
               <p>
                 From {product.priceRange.minVariantPrice.currencyCode}{" "}
                 {product.priceRange.minVariantPrice.amount}
               </p>
             </div>
           ))}
         </div>
       </div>
     );
   }
   ```

   **Nuxt example:**

   ```vue filename=~/slices/FeaturedCollection/index.vue collapsed
   <script setup lang="ts">
   import type { Content } from "@prismicio/client";
   import { isFilled } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import type { ShopifyCollection } from "~~/server/api/collections";

   const props = defineProps(
     getSliceComponentProps<Content.FeaturedCollectionSlice>(["slice"]),
   );

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${process.env.SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN!,
   });

   // useAsyncData provides universal data fetching (SSR + client-side navigation)
   const { data: collection } = await useAsyncData(
     `collection-${props.slice.primary.collection.handle}`,
     async () => {
       if (!isFilled.integration(props.slice.primary.collection)) {
         return null;
       }

       const shopifyCollection = props.slice.primary
         .collection as ShopifyCollection;

       // This runs on the server during SSR and on the client during navigation
       const response = await shopify.request(
         `#graphql
           query ($handle: String!) {
             collection(handle: $handle) {
               title
               description
               products(first: 10) {
                 edges {
                   node {
                     id
                     title
                     handle
                     priceRange {
                       minVariantPrice {
                         amount
                         currencyCode
                       }
                     }
                   }
                 }
               }
             }
           }
         `,
         {
           variables: {
             handle: shopifyCollection.handle,
           },
         },
       );
       return response.data?.collection;
     },
   );
   </script>

   <template>
     <div v-if="collection">
       <h2>{{ collection.title }}</h2>
       <p>{{ collection.description }}</p>
       <div>
         <div
           v-for="{ node: product } in collection.products.edges"
           :key="product.id"
         >
           <h3>{{ product.title }}</h3>
           <p>
             From {{ product.priceRange.minVariantPrice.currencyCode }}
             {{ product.priceRange.minVariantPrice.amount }}
           </p>
         </div>
       </div>
     </div>
   </template>
   ```

   **SvelteKit example:**

   ```ts filename=src/routes/[[preview=preview]]/[uid]/+page.server.ts collapsed
   // SvelteKit server-side data fetching strategy:
   // - All external API calls happen on the server in +page.server.ts
   // - Use mapSliceZone to enhance slices with additional data
   // - Components receive pre-fetched data as props (no client-side fetching)

   import type { PageServerLoad } from "./$types";
   import { createClient } from "$lib/prismicio";
   import { mapSliceZone, isFilled } from "@prismicio/client";
   import { createStorefrontApiClient } from "@shopify/storefront-api-client";
   import {
     SHOPIFY_STORE_DOMAIN,
     SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   } from "$env/static/private";

   const shopify = createStorefrontApiClient({
     storeDomain: `https://${SHOPIFY_STORE_DOMAIN}`,
     apiVersion: "2023-10",
     publicAccessToken: SHOPIFY_STOREFRONT_ACCESS_TOKEN,
   });

   export const load: PageServerLoad = async ({ params, fetch, cookies }) => {
     const client = createClient({ fetch, cookies });
     const page = await client.getByUID("page", params.uid);

     // mapSliceZone processes each slice type and allows data enhancement
     // This pattern scales well - add more slice types as needed
     const slices = await mapSliceZone(page.data.slices, {
       featured_collection: async ({ slice, ...otherProps }) => {
         if (!isFilled.integration(slice.primary.collection)) {
           return {
             slice,
             ...otherProps,
             collection: null,
           };
         }

         // Fetch collection data for this specific slice
         const response = await shopify.request(
           `#graphql
             query ($handle: String!) {
               collection(handle: $handle) {
                 title
                 description
                 products(first: 10) {
                   edges {
                     node {
                       id
                       title
                       handle
                       priceRange {
                         minVariantPrice {
                           amount
                           currencyCode
                         }
                       }
                     }
                   }
                 }
               }
             }
           `,
           {
             variables: {
               handle: slice.primary.collection.handle,
             },
           },
         );

         // Return the slice with additional data attached
         // This data will be passed as props to the slice component
         return {
           slice,
           ...otherProps,
           collection: response.data?.collection || null,
         };
       },
       // Add more slice handlers here as needed
       // e.g., product_grid: async ({ slice }) => { ... }
     });

     return { page, slices };
   };
   ```

   Use the `collection` prop added in `+page.server.ts`:

   ```svelte filename=src/lib/slices/FeaturedCollection/index.svelte collapsed
   <script lang="ts">
     import type { Content } from "@prismicio/client";
     import type { SliceComponentProps } from "@prismicio/svelte";
     import type { ShopifyCollection } from "$lib/../routes/api/collections/+server";

     type Props = SliceComponentProps<Content.FeaturedCollectionSlice> & {
       collection: {
         title: string;
         description: string;
         products: {
           edges: Array<{
             node: {
               id: string;
               title: string;
               handle: string;
               priceRange: {
                 minVariantPrice: {
                   amount: string;
                   currencyCode: string;
                 };
               };
             };
           }>;
         };
       } | null;
     };

     let { slice, collection }: Props = $props();
   </script>

   {#if collection}
     <div>
       <h2>{collection.title}</h2>
       <p>{collection.description}</p>
       <div>
         {#each collection.products.edges as { node: product }}
           <div>
             <h3>{product.title}</h3>
             <p>
               From {product.priceRange.minVariantPrice.currencyCode}
               {product.priceRange.minVariantPrice.amount}
             </p>
           </div>
         {/each}
       </div>
     </div>
   {/if}
   ```

6. **Add a collection to a page**

   Test your new field by selecting a product collection in a page. If everything was set up correctly, you should see the collection displayed on your page.

## Product collection metadata

The collection data returned by Prismic's Content API includes the following fields:

| Property    | Type   | Description                                                   | Default |
| ----------- | ------ | ------------------------------------------------------------- | ------- |
| id          | string | The collection's unique identifier from Shopify               | None    |
| handle      | string | The collection's URL handle (used for Storefront API queries) | None    |
| title       | string | The collection's display name                                 | None    |
| description | string | The collection's description text                             | None    |
| image       | object | The collection's image object containing the URL              | None    |

Here is an example of what an integration field containing a Shopify collection looks like from the Content API:

```json collapsed
{
  "id": "gid://shopify/Collection/123456789",
  "handle": "summer-collection",
  "title": "Summer Collection",
  "description": "Our latest summer arrivals featuring lightweight fabrics and vibrant colors",
  "image": {
    "url": "https://cdn.shopify.com/s/files/1/0612/3456/7890/collections/summer-collection-hero.jpg"
  }
}
```
