Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

Dev guideRecipesAPI ReferenceChangelog
Dev guideRecipesUser GuidesNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Skip and limit pagination

Use cases of skip and limit pagination, performance benefits, and implementation strategies.

Skip and limit pagination is a straightforward method for dividing large datasets into manageable pages. By specifying how many items to skip and how many to fetch, this approach is ideal for traditional page navigation and smaller datasets.

Use cases

Skip and limit pagination is best suited for the following scenarios where predictable, numbered pages are important:

  • Small to medium datasets – Efficient for fewer than 10,000 items.
  • Traditional page navigation – Ideal for "Page 1, Page 2, Page 3" style interfaces.
  • Admin panels and reports – Provides structured navigation and exact page numbers.

The following query retrieves a paginated list of articles, skipping a specified number of items and limiting the results. It sorts articles by their publish date in descending order.

query GetPagesWithSkipLimit($skip: Int = 0, $limit: Int = 10) {
  ArticlePage(
    limit: $limit,
    skip: $skip
    orderBy: { StartPublish: DESC }
  ) {
    items {
      Name
      RelativePath
      StartPublish
    }
    total
  }
}

Example variables to fetch page 3 with 10 items per page, skipping the first 20 items:

{
  "skip": 20,
  "limit": 10
}

Performance characteristics

Advantages

  • Simple implementation – Direct page number calculations.
  • Performant for typical use cases – Optimized for the first 10,000 results.
  • Predictable memory usage – Consistent resource consumption.
  • Easy navigation – Jump to any page directly.
  • Better for user interfaces – Page numbers such as "Go to page X".

Limitations

  • Performance degradation – Slows down with very large offsets (10,000+ skip values).
  • Inconsistent results – If content is added or removed during pagination.
  • Resource-intensive – For deep pagination in large datasets.

Real-world implementation

Skip and limit pagination is good for traditional page navigation, which is perfect for article listings, search results, and admin panels.

The following GraphQL query retrieves a paginated list of article pages filtered by category and sorted by publish date:

query GetArticlePages($page: Int = 1, $pageSize: Int = 10, $category: String) {
  ArticlePage(
    where: { Category: { eq: $category } }
    limit: $pageSize,
    orderBy: { StartPublish: DESC }
  ) {
    items {
      Name
      TeaserText
      RelativePath
      StartPublish
      Category
    }
    total
  }
}

The following React component handles pagination logic, fetches data using GraphQL, and renders navigation controls, including previous and next, and a page selector drop-down list:

function ArticlePagination({ category }) {
  const [currentPage, setCurrentPage] = useState(1);
  const pageSize = 10;
  
  const { data, loading, error } = useQuery(GET_ARTICLE_PAGES, {
    variables: { 
      page: currentPage, 
      pageSize, 
      category 
    }
  });

  const totalPages = Math.ceil(data?.ArticlePage?.total / pageSize);
  const hasNextPage = currentPage < totalPages;
  const hasPreviousPage = currentPage > 1;

  return (
    <div>
      <ArticleList items={data?.ArticlePage?.items} />
      
      <Pagination>
        <button 
          disabled={!hasPreviousPage}
          onClick={() => setCurrentPage(prev => prev - 1)}
        >
          Previous
        </button>
        
        <span>Page {currentPage} of {totalPages}</span>
        
        <button 
          disabled={!hasNextPage}
          onClick={() => setCurrentPage(prev => prev + 1)}
        >
          Next
        </button>
        
        {/* Jump to page functionality */}
        <select 
          value={currentPage} 
          onChange={(e) => setCurrentPage(Number(e.target.value))}
        >
          {Array.from({length: totalPages}, (_, i) => (
            <option key={i + 1} value={i + 1}>
              Page {i + 1}
            </option>
          ))}
        </select>
      </Pagination>
    </div>
  );
}

Performance optimization tips

Limit maximum skip values

Restrict the maximum skip value to prevent performance degradation. This ensures queries remain efficient without attempting to skip excessive items.

query SafeSkipLimit($skip: Int = 0, $limit: Int = 10) {
  ArticlePage(
    # Prevent performance issues with very large skip values
    skip: $skip
    limit: $limit
  ) {
    items { Name }
    total
  }
}

Use indexed fields for sorting

Enhance query performance by sorting with indexed fields. Indexed sorting is faster and more efficient than using non-indexed fields.

query OptimizedSorting {
  ArticlePage(
    # Use indexed fields for better performance
    orderBy: { StartPublish: DESC } # Indexed
    # Avoid: orderBy: { CustomTextField: ASC } # Not indexed
  ) {
    items { Name }
  }
}

Error handling and edge cases

The following function safely executes a paginated query, handles empty results, and redirects to the last valid page if the requested page exceeds the total number of pages:

async function safePaginatedQuery(page, pageSize) {
  try {
    const skip = (page - 1) * pageSize;
    
    const { data } = await client.query({
      query: PAGINATED_QUERY,
      variables: { skip, limit: pageSize }
    });
    
    // Handle empty results
    if (!data.ArticlePage.items.length && page > 1) {
      const totalPages = Math.ceil(data.ArticlePage.total / pageSize);
      if (page > totalPages) {
        // Redirect to last valid page
        return await safePaginatedQuery(totalPages, pageSize);
      }
    }
    
    return data;
  } catch (error) {
    console.error('Pagination error:', error);
    throw new Error('Failed to load page');
  }
}

Advanced pagination patterns

Enhance the user experience with these advanced pagination strategies for large datasets.

Hybrid approach for large datasets

Combine pagination methods to optimize user experience. The following example uses infinite scrolling for large datasets and traditional pagination for smaller ones.

function HybridPagination({ totalItems }) {
  const [viewMode, setViewMode] = useState('pages'); // 'pages' or 'infinite'
  const isLargeDataset = totalItems > 10000;

  if (isLargeDataset && viewMode === 'infinite') {
    return <CursorBasedInfiniteScroll />;
  } else {
    return <SkipLimitPagination maxPages={500} />; // Limit deep pagination
  }
}

Search results with smart pagination

Implement smart pagination for search results, adapting the method based on the number of results.

query SearchWithSmartPagination($searchTerm: String!, $skip: Int = 0, $limit: Int = 10) {
  Content(
    where: {
      _fulltext: { match: $searchTerm }
    }
    skip: $skip
    limit: $limit
    orderBy: { _ranking: SEMANTIC }
  ) {
    items {
      Name
      _score
      _typeName
      RelativePath
    }
    total
  }
}

Adaptation with result count

Switch to infinite scroll for large result sets, maintaining traditional pagination for smaller ones.

function SearchResultsPagination({ searchTerm }) {
  const [page, setPage] = useState(1);
  const pageSize = 10;
  
  const { data } = useQuery(SEARCH_QUERY, {
    variables: { searchTerm, skip: (page - 1) * pageSize, limit: pageSize }
  });

  const totalResults = data?.Content?.total || 0;
  
  // Switch to infinite scroll for large result sets
  if (totalResults > 1000) {
    return <InfiniteScrollResults searchTerm={searchTerm} />;
  }
  
  return <TraditionalPagination data={data} page={page} setPage={setPage} />;
}