In this article, we will see how to fetch all products of a Shopify store using pagination in Shopify Remix.
Introduction:
Pagination allows fetching an enormous number of products without using too many resources and going beyond the API rate limits. IIt fetches just a limited amount of products in one query and makes it possible to handle big data effectively.
Fetching the products in small batches reduces the overall latency of your request. You no longer have to wait for an answer that is too big; you can start processing the data as soon as each page arrives, which makes your application much smoother.
Getting products in batches reduces your application’s memory footprint.
You don’t need to store the entire dataset in memory at once, which is especially important for applications running on devices with limited resources.
Pagination can improve the performance of your application by distributing the workload across multiple requests. This helps prevent a timeout and keeps the application responsive even when a large set of data is in place.
Steps to Implement Pagination in Shopify to Get all Products:
Step 1: Create app.pagination route file in your app -> routes directory.
Step 2: Get all products using GraphQL.
For example
import { useLoaderData, Form, Link, useNavigate } from "@remix-run/react"; import shopify from "app/shopify.server"; import { json } from "@remix-run/node"; import { Page, IndexTable, Card, Pagination } from "@shopify/polaris"; import { useMemo} from "react"; export async function loader({ request }) { const { admin } = await shopify.authenticate.admin(request); const url = new URL(request.url); const searchParam = url.searchParams; const rel = searchParam.get('rel'); const cursor = searchParam.get('cursor'); let searchString = `first: 5`; if(cursor && rel) { if(rel == "next") { searchString += `, after: "${cursor}"`; } else { searchString = `last: 5, before: "${cursor}"`; } } const response = await admin.graphql(` { products(${searchString}) { pageInfo { endCursor hasNextPage hasPreviousPage startCursor } nodes { id title description status images(first: 1) { edges { node { originalSrc altText } } } } } }`); const parsedResponse = await response.json(); const product = parsedResponse.data.products.nodes; const pageInfo = parsedResponse.data.products.pageInfo; return json({ product, pageInfo }); }
In this loader function admin is destructured from the result of calling shopify.authenticate.admin(request). This probably returns an authenticated Shopify admin API instance.
The loader extracts the parameters rel and cursor from the request URL using URLSearchParams. These possibly indicate the direction of pagination (rel: “next” or “previous”) and the cursor (pagination token) to fetch the subsequent or previous set of the products.
Based on the rel and cursor parameters, the function constructs a GraphQL query string (searchString) to fetch products. If cursor and rel are present, it adds pagination parameters (after or before) to the GraphQL query to retrieve the next or previous set of products.
The function uses the method admin.graphql to send a GraphQL query to the Shopify admin endpoint. The query asks for product data, including id, title, description, the status of the product, and the original source and alternative text of the first image.
It also asks for pagination information (pageInfo): endCursor, hasNextPage, hasPreviousPage, and startCursor.
It returns a JSON parsed response. This function extracts product data (product) and pagination information (pageInfo) from the parsed response. It returns product data and pagination info in JSON format through json().
Step 3: A function named Shopdata has to be created to return the product data fetched using the loader function.
export default function Shopdata() { const { product, pageInfo} = useLoaderData(); const navigate = useNavigate(); const pagination = useMemo(() => { const { hasNextPage, hasPreviousPage, startCursor, endCursor } = pageInfo || {}; return { previous: { disabled: !hasPreviousPage || !startCursor, link: `/app/shopifydata/?rel=previous&cursor=${startCursor}`, }, next: { disabled: !hasNextPage || !endCursor, link: `/app/shopifydata/?rel=next&cursor=${endCursor}`, }, }; }, [pageInfo]); const rowMarkup = product.map( ({ images, id, title, description, status }, index) => ( <IndexTable.Row id={id} key={id} position={index}> <IndexTable.Cell> <img style={{ width: "30px", height: "40px" }} src={images.edges[0].node.originalSrc} alt={images.edges[0].node.altText} /> </IndexTable.Cell> <IndexTable.Cell> {id.replace("gid://shopify/Product/", "")} </IndexTable.Cell> <IndexTable.Cell> {title} </IndexTable.Cell> <IndexTable.Cell> {description} </IndexTable.Cell> <IndexTable.Cell> {status} </IndexTable.Cell> </IndexTable.Row> ) ); return ( <Page> <Card> <IndexTable itemCount={product.length} headings={[ { title: "Image" }, { title: "Id" }, { title: "Title" }, { title: "Description" }, { title: "Status" }, ]} selectable={false} > {rowMarkup} </IndexTable> <div className="navigation"> <Pagination hasPrevious={!pagination.previous.disabled} onPrevious={() =>navigate(pagination.previous.link)} hasNext={!pagination.next.disabled} onNext={() => navigate(pagination.next.link)} /> </div> </Card> </Page> ); }
Destructuring Loader Data using the useLoaderData hook to extract product and pageInfo from the data loaded by the loader function. This data possibly has the product details and pagination information.
Navigation Setup using the useNavigate hook to get the navigate function, which will be used for navigating between pagination pages.
Pagination Calculation calculates the pagination details using the useMemo hook depending on the pageInfo. Whether there is a previous page or a next page it makes the decision depending on hasPreviousPage and hasNextPage inside the pageInfo. This constructs pagination links (previous and next) with proper cursor values in order to fetch previous or next products set.
The Pagination Controls render the component from Shopify Polaris UI library
The Pagination component receives props of whether it has a previous and a next page through hasPrevious and hasNext properties as well as onPrevious and onNext to handle navigating. Whether the pagination controls are disabled or not is decided based on the pagination calculation made earlier.
Rendering the component returns a Page component with a Card component wrapped in it.
Conclusion:
In summary, pagination is among the important tools that help optimize a Shopify Store. It breaks up large data into smaller chunks, helping improve website performance, increase user experience, and makes it easier for visitors to go through products in the store. With Shopify Remix, a store owner will have an opportunity to customize his pagination settings according to the specific needs of his or her store to keep that website fast, responsive, and friendly. No matter how big or small the store is, pagination in Shopify Remix will help you improve your website’s performance and provide your customers with a better shopping experience.