• Sumit Sute
  • about
  • Work
  • Bloq
  • Byte
  • Blip
Bengaluru, In
All Bloqs
xxx
Mar 16, 2026
3 min read
The Great Scroll of 2026: Why I Finally Paginated My Tiny Website
A deep dive into why I spent three days implementing server-side pagination for a blog that currently has exactly twelve readers, and why it was actually a good idea (I think).
#typescript
#nextjs
#supabase
#performance
#engineering

The "Everything is Fine" Fallacy

For the longest time, my website followed the "Greedy Bastard" architectural pattern. When you hit the /byte or /bloq pages, the server would simply grab every single row from the database, shove it into a giant JSON blob, and hurl it at your browser like a digital snowball.

"It's fast!" I told myself, ignoring the fact that I only had ten posts. "It's simple!" I whispered as my Lighthouse score began to sweat.

But as my "short thoughts" (bytes) started to accumulate, I realized I was building a memory leak with a personality. Fetching 1,000 items into a React state is a great way to turn a user's phone into a very expensive hand warmer. It was time to grow up. It was time for Pagination.


The Philosophical Shift: Moving the Burden

The goal was simple: instead of the client-side filtering everything like a sieve, we move the brain to the database.

1. The Repository Pattern (Or: How to stop overworking your CPU)

I updated src/lib/byte/repository.ts to implement a proper repository pattern. The heavy lifting is now done by Supabase using .range().

The Decision: Why use .range() instead of just fetching everything and .slice()-ing in JavaScript? Because JavaScript is for the UI, and the Database is for the data. By asking for only 10 items, we reduce the Network Payload from a potential megabyte to a few kilobytes.

Plus, using { count: 'exact' } allows the database to tell us exactly how many pages we have without us having to download the entire universe first. It's like asking a librarian for the number of books in a section instead of counting them yourself by taking them all home.


The "URL is the State" Manifesto

A lot of people love putting pagination state in a React useState hook. Those people are wrong, and here is why I am smarter (this time):

  1. Deep Linking: If you find a funny post on page 4, you should be able to send that link to a friend. If the state is in React memory, the link just goes to page 1.
  2. SSR Happiness: By using searchParams, Next.js can pre-render the specific page on the server. This is SEO gold.
  3. The "Back" Button: It actually works. Magic!

I built PaginationControls.tsx as a "dumb" component. It doesn't know about state; it just knows how to build URLs.


Performance: The "Actual" Wins

Let's talk about the Epistemic Gap (a fancy word I use to sound smart). The gap between "it feels fast" and "it is architecturally sound."

  • Memory Pressure: By only fetching 10 items, the server-side rendering (SSR) runtime doesn't have to serialize a massive object. This keeps the heap small and the response times snappy.
  • Database Slicing: Filtering with .ilike() on the database level means we aren't sending 1,000 rows across the wire just to show the 3 that match "typescript".
  • Predictable Latency: Whether I have 10 posts or 10,000, the time to fetch one page remains relatively constant. (O(log n) indexing vs O(n) scan, for my fellow nerds).

What Could Go Wrong? (The "Room for Improvement" Section)

Because I am a perfectionist who also loves naps, there are things I could have done but didn't:

  1. Cursor-based Pagination: Currently, I use offset-based pagination (.range()). If someone adds a new post while you're on page 1, and you click page 2, you might see the same post twice. This is called the "Shifting Content Problem." Cursor-based pagination (using the ID of the last item) fixes this, but it makes the math way harder. For a blog with 12 readers, I decided my sanity was more important than the rare double-view of a short update.
  2. Prefetching: I could prefetch the next page when the user hovers over the "Next" button. This would make it feel "instant." Currently, it just feels "fast enough."
  3. Search Debouncing: The search bar currently hits the database every time the query param changes. It's efficient, but adding a tiny debounce prevents me from accidentally DDOS-ing myself while typing "hello".

The Verdict

Was it overkill? Probably. Do I feel better knowing that my site can now handle 1,000,000 bytes without crashing? Absolutely.

The lesson here is that even for small projects, Repository Patterns and URL-driven State are worth the extra hour of work. They turn a "hacky" site into a "system."

Now, if you'll excuse me, I need to go write a 13th byte so I can finally see that "Next" button in action on production.

Dec 13, 2025
6 min read
xxx
So I Built My Own View Counter
This is the simple, surgical system I built using Next.js and a robust Postgres function to ensure the numbers are always accurate.
Dec 9, 2025
4 min read
xxx
The Ghost in the Footer
A small story about making a static Next.js site feel a little less alone, and the detours through RLS logic and serverless edges that taught me why trust has to live on the server.