Skip to content

Blog x Pagination

Published: September 8, 2024

Reading time: 6 min


Blog x Pagination

A few weeks after I started working at Appwrite, I was assigned a task that I was very happy to take upon! Matter of fact, I was the one who created this feature request on the website repo because it really affected the blog page's humongous load.


Challenges & Learnings

  • Initial Approach

    My first strategy was to segment the blog posts into batches of 12, navigated through a query parameter (?page=2). Here's how I initially set up the logic:

    +page.svelte#script
    javascript
    const POSTS_PER_PAGE = 12;
    const pages = chunkArray(data.posts, POSTS_PER_PAGE);
    
    function chunkArray(array: Array<any>, chunkSize: number) {
        const chunks = [];
        for (let i = 0; i < array.length; i += chunkSize) {
            chunks.push(array.slice(i, i + chunkSize));
        }
        return chunks;
    }
    
    // using this for viewing.
    $: blogPosts = (index: number) => {
        return pages[index] ?? pages[0];
    };
  • Updated Approach

    Next up, I realized this wasn't the best way and could be significantly improved on the server side. So I changed the logic to return the chunked pages and then handle its navigation on the client side. My base logic was still something like this:

    +page.svelte#onMount
    javascript
    onMount(() => {
        return page.subscribe((page) => {
            const pageParam = page.url.searchParams.get('page') || '1';
            currentPage = Math.max(parseInt(pageParam), 1);
            // Utilize data.posts[currentPage] for rendering.
        });
    });
  • Team Lead's Insight!

    This is where my team lead @TorstenDittmann provided crucial feedback -

    We should do server side pagination via URLs 👍🏻

    This will be more efficient.

    Initially, I was a bit puzzled about how to further enhance the server-side logic? (In my defense, Svelte was new territory for me). After a few back-and-forth messages, I finally understood what he meant 🚀!

  • The final improvements

    The concept of pre-rendering pages seemed like a much better solution, with faster navigation without reloading content! For instance, accessing /blog, /blog/2, blog/3, etc., as pre-rendered HTML pages.


Key Learnings from Svelte

  • Insight 1

    So, I had this piece of logic in my source -

    +page.ts#load
    javascript
    export const load = async ({ params }) => {
        const currentPage = parseInt(params.page || '1', 10);
    
        if (params.page === '1') {
            redirect(302, '/blog');
        }
    
        const { posts, authors } = getAllBlogEntriesWithAuthors();
        const totalPages = Math.ceil(posts.length / BLOG_POSTS_PER_PAGE);
    
        const pageNavigationChunks = generatePageNavigation(currentPage, totalPages);
    
        const endIndex = currentPage * BLOG_POSTS_PER_PAGE;
        const startIndex = (currentPage - 1) * BLOG_POSTS_PER_PAGE;
        const blogPosts = posts.slice(startIndex, endIndex);
    
        return {
            authors,
            totalPages,
            currentPage,
            posts: blogPosts,
            navigation: pageNavigationChunks,
            featured: posts.find(post => post.featured)
        };
    };

    Notice anything? Hmm, neither did I at first. Identifying the issue proved challenging!


    The issue: The build process went into an infinite loop in order to build the pages because we did not specify what to do if the page wasn't available.

    This meant that if the end page was 13, the build process kept on creating pages till infinity and that caused an OOM on tests!

    No worries, we can do a quick fix like this -

    javascript
    if (blogPosts.length === 0) {
        error(404, 'Not Found');
    }
  • Insight 2

    Well the above fixed the OOM, but now I had this Page 0 and Page 14 Not Found Errors.
    So interestingly, if you define a URL in the component, it's supposed to exist and I definitely did not know that.

    I had these in my pagination anchor tags -

    +page.svelte#nav-anchor-tags
    javascript
    <a 
      data-sveltekit-noscroll
      class="flex navigation-button"
      href="/blog/{data.currentPage - 1}"
      class:navigation-button-active={!isFirstPage}
    >
      <span class="web-icon-chevron-left" style="font-size: 20px" />
      Previous
    </a>
    
    
    <a 
      data-sveltekit-noscroll
      class="flex navigation-button"
      href="/blog/{data.currentPage + 1}"
      class:navigation-button-active={!isFirstPage}
    >
      Next
      <span class="web-icon-chevron-right" style="font-size: 20px" />
    </a>

    This meant (for Svelte) that the pages 0 & 14 must exist. A simple hot-fix was to check if the URLs were in range, use if/else on anchor tags visibility!


The refinements not only improved site performance but also deepened my understanding of Svelte's capabilities.

Darshan Pandya