Legacy Hydrogen
These pages refer to Hydrogen versions 2026-04 and earlier. For versions 2026-10 and later, refer to the latest Hydrogen docs. If you're not using the latest version of Hydrogen, then we recommend that you migrate your storefront. To learn more, refer to Migrate from legacy Hydrogen.
Infinite scroll for collections in Hydrogen
This recipe implements infinite scroll functionality on collection pages using the Intersection Observer API.
Key features:
- Automatic loading when "Load more" button comes into view using Intersection Observer API
- Preserves browser history and URL state (replace mode to avoid clutter)
- Maintains scroll position during navigation
- Optimized loading with eager/lazy image loading (first 8 products eager, rest lazy)
Anchor to RequirementsRequirements
- Basic understanding of React hooks (useEffect, custom hooks)
- Familiarity with Shopify's Pagination component
- Knowledge of intersection observer API concepts
Anchor to Step 1: Document infinite scroll in the READMEStep 1: Document infinite scroll in the README
Update the README file with infinite scroll documentation and implementation details.
File
-# Hydrogen template: Skeleton
+# Hydrogen template: Infinite Scroll
-Hydrogen is Shopify’s stack for headless commerce. Hydrogen is designed to dovetail with [Remix](https://remix.run/), Shopify’s full stack web framework. This template contains a **minimal setup** of components, queries and tooling to get started with Hydrogen.
+This Hydrogen template demonstrates infinite scroll pagination for collection pages. Hydrogen is Shopify's stack for headless commerce, designed to work with [Remix](https://remix.run/), Shopify's full stack web framework.
+
+This template shows how to implement a seamless browsing experience where products automatically load as users scroll down, replacing traditional pagination with continuous content loading.
[Check out Hydrogen docs](https://shopify.dev/custom-storefronts/hydrogen)
[Get familiar with Remix](https://remix.run/docs/en/v1)
- Prettier
- GraphQL generator
- TypeScript and JavaScript flavors
-- Minimal setup of components and routes
+- **Infinite scroll pagination**
+- **Intersection Observer implementation**
+- **Optimized image loading strategies**
+
+## Infinite Scroll Features
+
+### Automatic Loading
+- Products load automatically when the "Load more" button enters the viewport
+- No manual clicking required for pagination
+- Smooth, uninterrupted browsing experience
+
+### Performance Optimizations
+- First 8 products load eagerly for instant display
+- Subsequent products use lazy loading
+- Images optimized with proper loading strategies
+- Minimal JavaScript overhead using native Intersection Observer
+
+### User Experience
+- Preserves browser history and URL state
+- Maintains scroll position during navigation
+- Clean URL updates using replace mode
+- No history cluttering from pagination
## Getting started
npm create @shopify/hydrogen@latest
```
+## Implementation Details
+
+The infinite scroll implementation uses:
+- React's `useEffect` hook for scroll detection
+- Intersection Observer API for viewport detection
+- Remix's navigation for URL updates
+- Shopify's Pagination component as the base
+
+### Key Components
+
+```tsx
+// Intersection Observer setup
+useEffect(() => {
+ if (!fetcher.data && !fetcher.state) {
+ fetcher.load(nextPageUrl);
+ }
+}, [inView]);
+```
+
## Building for production
```bash
npm run dev
```
+## Customization
+
+You can adjust the infinite scroll behavior by:
+- Changing the threshold for when loading triggers
+- Modifying the number of products loaded per batch
+- Customizing the loading indicator
+- Adding scroll-to-top functionality
+
## Setup for using Customer Account API (`/account` section)
-Follow step 1 and 2 of <https://shopify.dev/docs/custom-storefronts/building-with-the-customer-account-api/hydrogen#step-1-set-up-a-public-domain-for-local-development>
+Follow step 1 and 2 of <https://shopify.dev/docs/custom-storefronts/building-with-the-customer-account-api/hydrogen#step-1-set-up-a-public-domain-for-local-development>
\ No newline at end of file
Anchor to Step 2: Add infinite scroll to collectionsStep 2: Add infinite scroll to collections
Implement automatic loading with Intersection Observer when users scroll to the bottom.
File
-import {redirect, useLoaderData} from 'react-router';
+import {redirect, useLoaderData, useNavigate} from 'react-router';
import type {Route} from './+types/collections.$handle';
-import {getPaginationVariables, Analytics} from '@shopify/hydrogen';
-import {PaginatedResourceSection} from '~/components/PaginatedResourceSection';
+import {
+ getPaginationVariables,
+ Analytics,
+ Pagination,
+} from '@shopify/hydrogen';
import {redirectIfHandleIsLocalized} from '~/lib/redirect';
import {ProductItem} from '~/components/ProductItem';
+import {useEffect} from 'react';
+import {useInView} from 'react-intersection-observer';
import type {ProductItemFragment} from 'storefrontapi.generated';
export const meta: Route.MetaFunction = ({data}) => {
export default function Collection() {
const {collection} = useLoaderData<typeof loader>();
+ const {ref, inView} = useInView();
return (
<div className="collection">
<h1>{collection.title}</h1>
<p className="collection-description">{collection.description}</p>
- <PaginatedResourceSection<ProductItemFragment>
- connection={collection.products}
- resourcesClassName="products-grid"
- >
- {({node: product, index}) => (
- <ProductItem
- key={product.id}
- product={product}
- loading={index < 8 ? 'eager' : undefined}
- />
+ <Pagination<ProductItemFragment> connection={collection.products}>
+ {({
+ nodes,
+ isLoading,
+ PreviousLink,
+ NextLink,
+ state,
+ nextPageUrl,
+ hasNextPage,
+ }) => (
+ <>
+ <PreviousLink>
+ {isLoading ? 'Loading...' : <span>↑ Load previous</span>}
+ </PreviousLink>
+ <ProductsGrid
+ products={nodes}
+ inView={inView}
+ hasNextPage={hasNextPage}
+ nextPageUrl={nextPageUrl}
+ state={state}
+ />
+ <br />
+ {/* @description Add ref to NextLink for intersection observer */}
+ <NextLink ref={ref}>
+ {isLoading ? 'Loading...' : <span>Load more ↓</span>}
+ </NextLink>
+ </>
)}
- </PaginatedResourceSection>
+ </Pagination>
<Analytics.CollectionView
data={{
collection: {
);
}
+// @description ProductsGrid component with infinite scroll functionality
+function ProductsGrid({
+ products,
+ inView,
+ hasNextPage,
+ nextPageUrl,
+ state,
+}: {
+ products: ProductItemFragment[];
+ inView: boolean;
+ hasNextPage: boolean;
+ nextPageUrl: string;
+ state: any;
+}) {
+ const navigate = useNavigate();
+
+ useEffect(() => {
+ if (inView && hasNextPage) {
+ void navigate(nextPageUrl, {
+ replace: true,
+ preventScrollReset: true,
+ state,
+ });
+ }
+ }, [inView, navigate, state, nextPageUrl, hasNextPage]);
+
+ return (
+ <div className="products-grid">
+ {products.map((product, index) => {
+ return (
+ <ProductItem
+ key={product.id}
+ product={product}
+ loading={index < 8 ? 'eager' : undefined}
+ />
+ );
+ })}
+ </div>
+ );
+}
+
const PRODUCT_ITEM_FRAGMENT = `#graphql
fragment MoneyProductItem on MoneyV2 {
amount
Anchor to Step 3: Install Intersection Observer libraryStep 3: Install Intersection Observer library
Add the react-intersection-observer package for viewport detection.
"isbot": "^5.1.22",
"react": "18.3.1",
"react-dom": "18.3.1",
+ "react-intersection-observer": "^8.34.0",
"react-router": "7.9.2",
"react-router-dom": "7.9.2"
},
Was this page helpful?