← All Blogs

Rebuilding My Static Blog with Build-Time Data and Instant Search

2 February, 2026
~0 reads
Rebuilding My Static Blog with Build-Time Data and Instant Search blog

Static sites are supposed to be fast, simple, and reliable. But over time, my personal blog started behaving like a dynamic app - runtime API calls, pagination logic everywhere, and fragmented view counts spread across platforms.

Last week, I rebuilt the blog section of ravgeet.in (Nuxt.js) to fix this properly. The end result is still a static site, but now it feels alive: aggregated view counts, instant search and sorting, and zero runtime dependencies on external APIs.

This post walks through the thinking, architecture, and trade-offs behind that rebuild.

The problem with my old setup

Originally, my blog worked like this:

  • Blog content lived on Hashnode (canonical source)

  • Some posts were also cross-posted to Dev.to

  • Pages fetched blog data at runtime using Hashnode’s GraphQL API

  • Pagination logic (hasNextPage, cursors) lived inside the UI

This had a few downsides:

  • A static site depending on live APIs felt wrong

  • Local development and builds were slower and flaky

  • Adding features like search or sorting would require more APIs

I wanted the blog to stay static - but smarter.

Build-time data as a contract

The core decision was simple:

Move all external data fetching to build time, and treat the result as immutable static data.

Instead of fetching blogs at runtime, I introduced a build step that:

  1. Fetches blogs from Hashnode

  2. Fetches articles from Dev.to

  3. Matches the same article across platforms

  4. Aggregates view counts

  5. Writes everything into a single JSON file

At runtime, the site only reads from that JSON.

Hashnode + Dev.to
        ↓
Build-time fetch & normalize
        ↓
static/blogs.json
        ↓
Nuxt UI (search, sort, views)

This one decision simplified everything else.

Fetching and aggregating blog data

Hashnode: canonical content

Hashnode remains the source of truth for:

  • Title, slug, content, tags

  • Publish date

  • Cover image

  • Base view count

I fetch all posts using Hashnode’s GraphQL API with pagination handled inside a Node.js script.

Dev.to: distribution and extra reach

Dev.to is where additional readers come from, so ignoring those views felt wrong.

Using the Dev.to API (with a personal access token), I fetch all my articles and extract:

  • slug

  • canonical_url

  • page_views_count

Matching articles across platforms

This is the tricky part. Articles are matched using a layered strategy:

  1. Slug match

  2. Canonical URL match

  3. Title match

Once matched, the final view count becomes:

combinedViews = hashnodeViews + devtoViews

The output for each blog includes:

  • Combined views

  • Platform-specific views (for debugging)

  • Dev.to URL (if matched)

Writing the static data contract

All processed data is written to the static/blogs.json file.

This file is:

  • Generated at build time

  • Git-ignored

  • Treated as read-only by the app

It also includes metadata like the last updated time and the total blog count.

This JSON file effectively replaces my entire blog API.

Replacing runtime APIs with static services

Previously, services/blogs.js made live GraphQL calls. After the refactor:

  • The service dynamically imports blogs.json

  • find, findOne, and search all operate locally

  • No Axios

  • No pagination state

  • No network failures

From the UI’s perspective, nothing changed - but under the hood, everything became predictable.

Instant search and sorting

Once all blog data is local, search becomes trivial.

I added:

  • Client-side text search (title, brief, tags)

  • Sorting by:

    • Published date (recent / oldest)

    • View count (most / least)

Because the dataset is small and static:

  • Search results are instant

  • No debouncing hacks

  • No loading states

  • Sorting is deterministic

This dramatically improves discoverability without introducing a search service.

Trade-offs and lessons learned

This approach isn’t perfect:

  • Build time increases slightly

  • The JSON file grows over time

  • It’s not suitable for real-time analytics

But for a personal blog, the trade-offs are worth it.

The key takeaways from the refactor that made me realize that:

  • Static doesn’t mean lifeless

  • Build-time data pipelines are underrated

  • One clean data contract simplifies UI, UX, and performance

If you’re curious, the full implementation lives in the ravgeet.in repository.

📮 Join my newsletter

I share tips on how to get started with freelancing, remote jobs, developer-related stuff, startup ecosystem, and lots of insider secrets with my subscribers.

Subscribe →
🧠 More Interesting Reads
Disabling Submit button if Form fields have not changed in a Nuxt/Vue app
Learn how to keep a form submit button disabled until the form fields have not changed in a Nuxt/Vue app.
Improve Table Speed in React by Using Web Workers for Filters
Turbocharge your React tables by moving expensive filters off the main thread with Web Workers.
Creating an App Information Component in Nuxt
Learn how to create a component to show the App Version and Last Updated At time in a Nuxt app.