Building a directory site sounds trivial until you find yourself wiring up databases, caching layers, and reactive front-end frameworks just to display what is essentially a curated table of contents. I recently built Social Tools List, a site for comparing social media software. My goal was to get it online quickly, keep it fast, and avoid maintaining infrastructure for content that only changes when I add or update a tool. I settled on a static-first stack: Astro for site generation, TypeScript for structured data, and Cloudflare Workers for deployment. The result is a site that loads instantly, costs almost nothing to host, and requires no database administration.
Why Static-First Makes Sense for a Directory
Many web apps default to server rendering or single-page architectures because they feel like safe, modern choices. But not every site receives dynamic user input on every request. Social Tools List is a read-heavy resource. The comparison data changes when I push an update, not when a visitor hits refresh. Rendering HTML ahead of time removes the need for database queries at the edge, template compilation on the fly, or hydration overhead in the browser. I generate the site at build time, deploy the static files, and let a lightweight worker handle the wrapper. This keeps response times low and eliminates an entire class of runtime failures.
Store Data in TypeScript, Not a Database
I do not use a database. Every tool in the directory is defined as a TypeScript object with a slug, name, domain, and an array of supported workflows. A typical entry looks like this:
{
slug: 'buffer',
name: 'Buffer',
domain: 'buffer.com',
workflows: ['scheduling', 'analytics']
}
Storing data in the same language that builds the site has two immediate benefits. First, pull requests become content reviews. When I add a tool, the diff shows the exact fields and values, and a teammate can spot a typo or a wrong domain without learning a CMS interface. Second, the TypeScript compiler enforces the shape of every record. If I forget to include a slug or misspell a workflow key, the build fails before the bad data ever reaches a page.
A database would introduce migrations, connection strings, caching strategies, and backup routines. For a directory with a few hundred entries that I maintain manually, that overhead is pure drag. Static data in TypeScript modules is the cheapest correct choice for this model. The "correct" part matters. It is not just about saving money; it is about removing abstraction layers that solve problems I do not have.
Let Astro Own the Routing and Rendering
Astro generates one HTML page per tool from a single dynamic route. I define one layout component, and Astro stamps out the metadata, headings, and structured data for every entry automatically. Because the same dataset drives the main index, the workflow category hubs, and the individual detail pages, there is no chance that a card on the home page shows a different description than the detail page itself. In traditional CMS setups, you often see drift: the API returns one version, the cache another, and a client-side render a third. Static generation from a single source of truth prevents that.
Astro’s island architecture also makes it easy to add small interactive pieces without poisoning the whole page with JavaScript. The site ships as static HTML, and only the filtering script hydrates its specific corner of the DOM. There is no framework runtime wrapping the entire document. Astro also treats page metadata as a first-class concern. Each tool page gets its own title tag and meta description derived directly from the TypeScript record, so I do not need a separate plugin or a head management library.
Filtering Without a Framework
Search and filtering often trigger developers to install React, Vue, or a heavy state management library. I resisted that. Astro renders the full list as plain HTML on the server. A small vanilla JavaScript script, less than a kilobyte, runs in the browser and toggles the display property of list items based on a workflow tag or a text match.
Das Ausliefern des vollständigen Markups klingt ineffizient, wenn man aus einem API-getriebenen Umfeld kommt. Aber betrachten Sie den Overhead eines typischen dynamischen Ansatzes. Der Browser lädt ein JavaScript-Bundle herunter, hydriert einen Komponentenbaum, ruft einen Endpunkt auf, wartet auf JSON und rendert dann die Zeilen. Für ein Verzeichnis, das weniger als hundert Tools auflistet, ist dieses Ritual langsamer und weniger zuverlässig, als Divs auszublenden, die bereits im Dokument vorhanden sind. Mein Skript hängt Event-Listener an die Filter-Buttons an, liest ein data-workflow-Attribut in jeder Zeile aus und setzt Nicht-Treffer auf hidden. Der Vorgang dauert Millisekunden.
Da die Liste bereits im initialen HTML vorhanden ist, ist die Website auch ohne JavaScript nutzbar. Suchmaschinen-Crawler sehen jeden Link und jede Beschreibung. Nutzer in langsamen Netzwerken oder mit Script-Blockern erhalten dennoch das vollständige Verzeichnis. Das Filtern ist eine Erweiterung, kein Hindernis.
Sitemaps und Robots als Code
Sitemaps und robots.txt sind keine nachträglichen Gedanken, die von Hand geschrieben werden. Sie sind Astro-Routen, die denselben Datensatz und dieselben URL-Helper verwenden wie
