Skip to content

[FEATURE] Add pagination to blog page #4938

@jayvaliya

Description

@jayvaliya

Why do we need this improvement?

The blog index page currently loads and renders all 140+ blog posts simultaneously. This creates several issues:

  1. Performance: The initial page load renders over 7,000 DOM nodes, which impacts load time and rendering performance, especially on lower-end devices and slower connections.

  2. User Experience: Without pagination, users cannot easily browse posts chronologically in manageable chunks. The current "load everything" approach makes it difficult to navigate through the blog archive.

  3. Scalability: As the blog continues to grow, the performance impact will worsen. Adding 10 new posts per month means the DOM will keep expanding indefinitely.

  4. Filter Interaction: When filters are applied, users can still see 50-100 posts at once, which is overwhelming and makes it hard to focus on relevant content.

Currently, users have to scroll through a potentially massive grid to find older posts, and the page becomes sluggish when all cards are rendered simultaneously.

How will this change help?

Implementing pagination will provide several benefits:

Performance Improvements:

  • Reduce initial DOM nodes by approximately 95% (from 7,179 to ~360 nodes with 12 posts per page)
  • Faster initial page load and rendering
  • Improved scroll performance
  • Better performance on mobile devices

User Experience:

  • Clear navigation through blog archive with page numbers
  • Ability to browse chronologically in digestible chunks
  • Visual feedback showing "Showing X-Y of Z posts"
  • Better focus on the current set of posts

Technical Benefits:

  • Integrates seamlessly with the existing filter system
  • Uses URL query parameters for shareable links
  • Maintains browser history (back/forward buttons work correctly)
  • No breaking changes to existing functionality

The pagination will work in conjunction with the existing filter system, where filters are applied first, and then pagination divides the filtered results into pages.

Screenshots

Current Performance Metrics (from Chrome DevTools):

Screenshot shows the blog page performance analysis

Image

revealing:

  • 7,179 DOM nodes currently rendered (shown at bottom: "Nodes [6 - 7179]")
  • 165 event listeners attached to elements (shown at bottom: "Listeners [11 - 165]")
  • 754ms spent on scripting during page load
  • 96ms spent on rendering
  • Total load time: 2.37 seconds
  • Performance warnings for Documents (2 + 9) and Listeners count

The high node count and listener count indicate that rendering all 140+ blog posts simultaneously creates unnecessary DOM overhead. With pagination showing only 12 posts at a time, we would reduce this to approximately:

  • ~360 DOM nodes (95% reduction)
  • ~14-20 event listeners (88% reduction)
  • Significantly reduced scripting and rendering time

How could it be implemented/designed?

Design Approach:

Implement client-side pagination that integrates with the existing architecture:

1. Pagination Configuration:

  • Posts per page: 12 (matches 4 rows × 3 columns grid layout)
  • Page numbers stored in URL query parameter: ?page=2
  • Default to page 1 if no parameter provided

2. Component Structure:
Create a new BlogPagination component in components/navigation/BlogPagination.tsx (separate from the existing dashboard pagination component to maintain separation of concerns):

interface BlogPaginationProps {
  currentPage: number;
  totalPages: number;
  totalPosts: number;
  postsPerPage: number;
  onPageChange: (page: number) => void;
}

3. State Management Flow:

User loads page
  ↓
URL: /blog?page=2&type=Community
  ↓
useRouter reads query params
  ↓
Extract: page=2, filters={type: 'Community'}
  ↓
Apply filters to all posts → filtered array
  ↓
Calculate: totalPosts, totalPages, startIndex, endIndex
  ↓
Slice array: filtered.slice(startIndex, endIndex)
  ↓
Render: 12 posts + pagination controls

4. Integration with Existing Code:

In index.tsx:

  • Add currentPage state derived from URL query parameters
  • Modify existing posts state to become displayedPosts after slicing
  • Add pagination calculations after filter logic
  • Reset to page 1 when filters change
  • Update clearFilters function to also clear page parameter

5. Pagination UI Elements:

  • Previous button (disabled on page 1)
  • Smart page number display (e.g., "1 ... 4 5 [6] 7 8 ... 12")
  • Next button (disabled on last page)
  • Info text: "Showing X-Y of Z posts"
  • Scroll to top of blog grid on page change

6. URL Management:

  • Use Next.js router with shallow routing: router.push(url, undefined, { shallow: true })
  • Preserve existing query parameters (filters) when changing page
  • Validate page numbers (redirect invalid pages to page 1)
  • Update URL without page reload

7. Edge Cases Handled:

  • Empty state when filters result in 0 posts (already handled)
  • Single page when filtered posts ≤ 12
  • Page number validation (handle ?page=999 or ?page=0)
  • Filter changes reset to page 1
  • Hide pagination controls if total pages = 1

8. Accessibility:

  • Use semantic <nav> element for pagination
  • Add ARIA labels: aria-label="Pagination", aria-current="page"
  • Ensure keyboard navigation works (tab through controls)
  • Maintain focus management on page change

9. Testing:

  • Unit tests for pagination calculations and edge cases
  • Cypress E2E tests for page navigation and filter integration
  • Test browser back/forward functionality
  • Add data-testid attributes for test selectors

10. TypeScript Types:
Create types in types/components/navigation/BlogPagination.ts for proper type safety throughout the implementation.

This approach reuses existing patterns (URL query params, filter integration, component structure) while keeping the implementation simple and performant.

🚧 Breaking changes

No

👀 Have you checked for similar open issues?

  • I checked and didn't find a similar issue

🏢 Have you read the Contributing Guidelines?

Are you willing to work on this issue?

Yes I am willing to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions