How to build this blog


This post walks through how this very blog is built: an Astro content site with Pagefind for static, client-side full-text search.

What you get

  • An Astro 6 content collection for posts authored in Markdown or MDX.
  • A production build that emits a static search index alongside the HTML.
  • A /search page powered by the Pagefind UI — no server, no API key, no third-party dependency at runtime.

1. Scaffold the Astro blog

Start from the official starter:

npm create astro@latest -- --template blog
cd my-blog
npm install

That gives you the content collection in src/content/blog/, a BlogPost layout, and routes for /, /blog, /blog/[slug], and /about.

2. Add Pagefind

Install Pagefind as a dev dependency and chain it onto the build script in package.json:

{
  "scripts": {
    "build": "astro build && pagefind --site dist"
  },
  "devDependencies": {
    "pagefind": "^1.4.0"
  }
}

Pagefind walks the built dist/ directory, parses the HTML, and writes a static index to dist/pagefind/. No config file is required for the default setup.

3. Tell Pagefind what to index

By default Pagefind indexes the entire <body> of every page. To restrict indexing to post content (and skip the header/footer), add data-pagefind-body to the <article> in src/layouts/BlogPost.astro:

<main>
  <article data-pagefind-body>
    {/* hero image, title, slot... */}
  </article>
</main>

Once any page on the site uses data-pagefind-body, Pagefind switches modes and only indexes pages that opt in.

4. Build a Search component

Pagefind ships a default UI bundle. Load it on demand from a small Astro component so it isn’t pulled into every page:

---
const { id = 'search' } = Astro.props;
---
<link rel="stylesheet" href="/pagefind/pagefind-ui.css" />
<div id={id}></div>
<script is:inline define:vars={{ id }}>
  window.addEventListener('DOMContentLoaded', () => {
    const script = document.createElement('script');
    script.src = '/pagefind/pagefind-ui.js';
    script.onload = () => new window.PagefindUI({ element: `#${id}` });
    document.head.appendChild(script);
  });
</script>

Then drop it onto a dedicated route at src/pages/search.astro and link to /search from the header.

5. Build and preview

npm run build
npm run preview

Open /search and start typing — results stream in from the static index. Because Pagefind only generates its index during astro build, search will be unavailable under astro dev; the component above shows a friendly placeholder in that case.

What about scaling?

Pagefind chunks its index by language and lazy-loads only the shards needed for a given query, so a blog with thousands of posts still ships kilobytes of JS to the user up front. For most personal sites you can ignore the index size entirely.

More