DEV Community

Meng Li
Meng Li

Posted on

How I Built a Tool Directory with Astro and Cloudflare Pages

A tool directory sounds simple: store some links, put them into categories, and add a search box.

Then you start adding tools. You need individual pages, pagination, working links, useful descriptions, and a way to ship updates without turning the project into a maintenance job.

That's the problem I've been working through with NavSprout, a small directory of web tools, creative apps, and browser-based projects.

I chose Astro for the content-heavy pages and Cloudflare Pages for deployment. Here's what has mattered so far—and what I'd do differently if I started again.

1. Static first, interactive where it counts

Most visitors come to a directory to do one of three things:

  • Discover something useful.
  • Check whether it fits their task.
  • Open the original tool.

None of those steps needs a server-rendered page for every request.

Astro makes a good default for this kind of site: generate the content pages ahead of time, then add client-side behavior only where it helps. On NavSprout, the directory includes filtering, search, saved tools, and comparisons, but the actual tool information should still be readable without waiting for a large JavaScript application to boot.

The important distinction is static content doesn't mean a static experience.

2. The data model matters more than the cards

My first instinct with a directory was to focus on the layout. In practice, deciding what information belongs to each tool entry matters much more.

A useful profile should answer:

  • What does this tool actually help me do?
  • Who is it a good fit for?
  • Why was it included?
  • What should I check before using it?
  • Where is the official source?

Here's a simplified example of the kind of structured data that supports those questions—not a copy of my production schema:

type Tool = {
  slug: string;
  name: string;
  category: string;
  useCase: string;
  whyHere: string;
  officialUrl: string;
  limitations?: string[];
};
Enter fullscreen mode Exit fullscreen mode

A directory page can look nice and still be unhelpful. The goal is to give someone a reason to click—or a reason to skip a tool that isn't right for them.

3. Static pagination without manually reshuffling pages

Pagination was one of the first problems I had to think through.

If new entries appear first, adding one tool changes which items belong on page 1, page 2, and so on. I didn't want to manually maintain those pages.

Astro's paginate() function handles the page generation at build time. The following is an illustrative version of the pattern, using 12 items per page:

---
// Example: src/pages/[...page].astro
import { tools } from '../data/tools';

export function getStaticPaths({ paginate }) {
  const newestFirst = [...tools].sort(
    (a, b) => Date.parse(b.addedAt) - Date.parse(a.addedAt)
  );

  return paginate(newestFirst, { pageSize: 12 });
}

const { page } = Astro.props;
---

<section>
  {page.data.map((tool) => (
    <a href={`/tools/${tool.slug}/`}>{tool.name}</a>
  ))}
</section>

<nav aria-label="Pagination">
  {page.url.prev && <a href={page.url.prev}>Previous</a>}
  <span>Page {page.currentPage} of {page.lastPage}</span>
  {page.url.next && <a href={page.url.next}>Next</a>}
</nav>
Enter fullscreen mode Exit fullscreen mode

With a rest parameter route like [...page].astro, the first page can live at /, followed by /2/, /3/, and so on. When the dataset changes, the next build regenerates the appropriate pages.

One detail worth keeping separate: paginated listing URLs are not the identity of a tool. Each tool should have its own stable, descriptive URL, so an item moving from page 1 to page 2 doesn't change its permanent address.

4. SEO: a directory needs more than lots of URLs

It's easy to generate hundreds of pages with Astro. It's much harder to make those pages worth visiting.

A few decisions have been especially important to me:

Give detail pages a real job. A tool page shouldn't just repeat its homepage tagline. Include the use case, selection rationale, source links, and limitations where known.

Use normal HTML links. Pagination and tool detail links should be actual anchors, not destinations that only appear after JavaScript runs.

Keep metadata specific. Titles and descriptions should reflect the individual tool or category. Avoid giving every page the same generic SEO copy.

Treat pagination honestly. Page 2 contains different tools from page 1, so it shouldn't automatically canonicalize to page 1. Each indexable listing page needs a sensible canonical strategy.

Don't confuse more pages with more value. If two entries describe essentially the same thing, adding both doesn't make the directory more useful.

A sitemap helps search engines discover generated routes, but it doesn't replace thoughtful internal linking or good content.

5. Deploying with Cloudflare Pages

For a mostly static Astro site, the deployment setup can be wonderfully boring.

The standard Cloudflare Pages configuration is:

Setting Value
Build command npm run build
Output directory dist
Production branch main (or your chosen branch)

With the repository connected, pushing a content update can trigger a new build and deployment. Preview deployments are useful for catching broken routes, bad metadata, or unexpected pagination changes before they go live.

The bigger benefit is operational simplicity. I don't want to manage a server just to serve directory pages. If I add something that genuinely needs a dynamic backend, I can evaluate that separately rather than making the entire site dynamic from day one.

6. What I'd pay attention to next

As the number of tools grows, the challenges shift:

  1. Content quality: keeping entries accurate as products change.
  2. Discovery: helping people find the right tool without scrolling through endless cards.
  3. Build health: catching duplicate slugs, broken links, and missing metadata before deployment.
  4. Useful signals: learning which pages actually help visitors, not just which pages get impressions.

If I were starting a directory today, I'd still choose a static-first approach. But I'd design the content schema and URL structure before polishing the homepage.

The main lesson so far: a directory is a content system first and a collection of links second.

If you've built a directory with Astro, I'm curious how you handle updates and pagination once the catalog grows.

Top comments (1)

Collapse
 
limeng05 profile image
Meng Li •

AINavly use the same way!