---
schema_version: "1"
id: 5b12aa49c1a5ff25452b7f74a99126d918c0640551d1805675e8db37288a544d
title: Add a Component That Fetches Data
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/sfnext/guides/sfnext-customize-data.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize-data.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 594db39339330ccf22d3c29caffef0776fc9bb7e994771a0bac11619da6dff40
products:
  - Composable Storefront
  - Salesforce APIs
---
# Add a Component That Fetches Data

Extend a Storefront Next page to fetch data on the server and pass it to a React component. This guide builds on [Add a Page and a Component](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize.md) by adding a server-side loader that retrieves products from B2C Commerce and renders them in a new component.

## Prerequisites

- A Storefront Next app created in Business Manager, which includes a SLAS client set up automatically for authorization to Shopper APIs. See [Create a Storefront in Business Manager with Local Code](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-bm.md) or [Explore Storefront Next Code Locally](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-quick-start-create-sf.md).
- A custom page created in [Add a Page and a Component](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-customize.md).
- An Integrated Development Environment (IDE), such as VS Code, Cursor, or your favorite IDE. See [Download VS Code](https://code.visualstudio.com/download) or [Download Cursor](https://cursor.com/download).

## Steps

1. Create a `FeaturedProducts` component that renders a grid of products.

   a. In `{project-folder}/src/components`, create a folder named `featured-products`.

   b. In the new `featured-products` folder, create `index.tsx` with this code.

   ```tsx
   import { type ReactElement } from 'react';

   export interface Product {
       productId: string;
       productName: string;
       priceMax?: number;
       image?: { link: string; alt: string };
   }

   export interface FeaturedProductsProps {
       products: Product[];
       title?: string;
   }

   export default function FeaturedProducts({ products, title = 'Featured Products' }: FeaturedProductsProps): ReactElement {
       return (
           <section className="container mx-auto px-4 py-8">
               <h2 className="text-2xl font-bold mb-6">{title}</h2>
               <ul className="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4">
                   {products.map((product) => (
                       <li key={product.productId} className="border rounded-lg p-4">
                           {product.image && (
                               <img
                                   src={product.image.link}
                                   alt={product.image.alt}
                                   className="w-full aspect-square object-cover rounded mb-2"
                               />
                           )}
                           <p className="font-medium text-sm">{product.productName}</p>
                           {product.priceMax && (
                               <p className="text-muted-foreground text-sm">${product.priceMax.toFixed(2)}</p>
                           )}
                       </li>
                   ))}
               </ul>
           </section>
       );
   }
   ```

   `FeaturedProducts` accepts a `products` array and an optional `title`. It renders a responsive grid of product cards, each showing the product image, name, and price.

2. Add a loader to the custom page route to fetch products from B2C Commerce. Open `{project-folder}/src/routes/_app.custom-page.tsx` and replace its contents with this code.

   ```tsx
   import { type ReactElement } from 'react';
   import { type LoaderFunctionArgs } from 'react-router';
   import { createPage, type RouteComponentProps } from '@/components/create-page';
   import CustomBanner from '@/components/custom-banner';
   import FeaturedProducts, { type Product } from '@/components/featured-products';
   import { createApiClients } from '@/lib/api-clients.server';

   export async function loader({ context }: LoaderFunctionArgs) {
       const clients = createApiClients(context);
       const { data } = await clients.shopperSearch.productSearch({
           params: {
               query: {
                   q: 'dress',
                   limit: 4,
                   allImages: true,
               },
           },
       });
       const products: Product[] = (data.hits ?? []).map((hit) => ({
           productId: hit.productId,
           productName: hit.productName ?? '',
           priceMax: hit.priceMax,
           image: hit.image
               ? { link: hit.image.link, alt: hit.image.alt ?? hit.productName ?? '' }
               : undefined,
       }));
       return { products };
   }

   export type CustomPageData = Awaited<ReturnType<typeof loader>>;

   function CustomPageView({ loaderData }: RouteComponentProps<CustomPageData>): ReactElement {
       return (
           <div className="pb-16">
               <CustomBanner title="Welcome to the Custom Collection!" />
               <FeaturedProducts products={loaderData.products} />
           </div>
       );
   }

   export default createPage<CustomPageData>({
       component: CustomPageView,
   });
   ```

   The `loader` function runs on the server before the page renders. `createApiClients(context)` returns all Shopper API clients configured for the current site. The `createApiClients` function is a template wrapper around the framework's `createCommerceApiClients` function. `createApiClients` reads site configuration, credentials, and locale from context and sets up authentication automatically so you don't need to pass those values manually. The loader calls the Shopper Search API to retrieve four products matching the query `dress` and destructures `data` from the response. The results are mapped to the `Product` type and returned as `loaderData`. `CustomPageView` receives `loaderData` as a prop and passes the products to `FeaturedProducts`. `CustomPageData` is derived directly from the loader's return type, so the component stays in sync with the data shape automatically.

3. In a terminal window, go to the storefront folder.

   ```bash
   cd {project-folder}
   ```

4. Build the storefront and run the development server.

   ```bash
   pnpm run build
   pnpm run dev
   ```

5. Navigate to `http://localhost:5173/custom-page`. The page now shows the banner followed by a grid of products fetched from B2C Commerce.

![Custom page shows the featured products that are fetched with the Shopper API](https://developer.salesforce.com/docs/media/sfnext/sfnext-product-grid.png '{"class": "image-xl"}')

## Next Steps

- [Deploy Storefront Next to Managed Runtime](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-push-mrt-auto.md)

## See Also

- [Data Fetching](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-data-loading.md)
- [SCAPI Client](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/sfnext-api-integration.md)
