Just recently, I was staring at a performance report for a client’s documentation site, feeling that sinking sensation we all know too well. Their beautifully designed React-based docs were scoring a dismal 54 on Lighthouse mobile performance. It’s an internal documentation so it’s not even high traffic but those are practically being used on a daily basis for references and training materials so time spent is money saved.
I love React, but sometimes we reach for our favorite hammer when what we really need is a screwdriver. That’s when I decided to completely rebuild their documentation using Astro paired with TailwindCSS – and the results blew me away.
Why I Fell in Love with Astro for Documentation
Have you ever used a tool that just feels right for a specific job? That’s Astro for documentation sites.
For years, I’d been building docs with everything from Gatsby to Next.js to custom solutions. They all worked, sure, but they always felt overengineered for the task. Documentation is primarily static content that needs to be fast, accessible, and SEO-friendly – exactly what Astro was born to do.
Here’s why this combination has become my go-to for documentation sites:
- Zero JavaScript by default – Astro only ships JS when you explicitly ask for it
- Markdown/MDX support that actually works – no weird edge cases or complex config
- Islands of interactivity where needed – search, interactive demos, etc.
- Framework-agnostic – I can use React components for interactive parts without committing the entire site to it
- TailwindCSS integration – rapid styling without context switching between files
After moving the client’s site to Astro + Tailwind, their Lighthouse score jumped to 96, and the site simply felt snappier. But enough about why – let’s get into the how.
Setting Up Your Docs Project in 5 Minutes
The first time I tried setting up an Astro project with Tailwind, I was shocked by how painless it was. Here’s how you can do the same:
# Create a new project with the Astro CLI
npm create astro@latest my-docs
# Navigate to your new project
cd my-docs
# Add TailwindCSS with a single command
npx astro add tailwind
# Start the dev server
npm run dev
That’s literally it. No manually configuring PostCSS, no installing a dozen dependencies – Astro’s integration system handles it all.
The Project Structure I’ve Found Works Best
After building several documentation sites with Astro, I’ve settled on a project structure that makes long-term maintenance a breeze:
docs-site/
├── src/
│ ├── components/ # Reusable UI components
│ ├── layouts/ # Page layouts
│ ├── content/ # Where your actual docs live
│ │ └── docs/
│ │ ├── getting-started/
│ │ └── advanced/
│ ├── pages/ # Routes and dynamic pages
│ └── styles/ # Global styles
This separation makes it easy for both developers (who work in components and layouts) and content writers (who work in the content directory) to collaborate without stepping on each other’s toes.
The Magic of Content Collections
One of my favorite Astro features that I now can’t live without is Content Collections. Before discovering them, I was manually wrangling frontmatter and building complicated directory-based navigation systems.
Here’s how I set them up for documentation:
// src/content/config.ts
import { defineCollection, z } from 'astro:content'
const docsCollection = defineCollection({
schema: z.object({
title: z.string(),
description: z.string(),
order: z.number().default(0),
draft: z.boolean().default(false),
}),
})
export const collections = {
docs: docsCollection,
}
What I love about this is the built-in type safety. If someone on the team forgets a required field or uses the wrong data type, Astro will catch it during build rather than it becoming a runtime issue.
Building a Responsive Documentation Layout
For documentation sites, I’ve found that a three-column layout works wonderfully – navigation sidebar on the left, content in the middle, and (on desktop) a table of contents on the right.
Here’s a simplified version of a layout I use frequently:
<!-- src/layouts/DocLayout.astro -->
---
import MainLayout from './MainLayout.astro';
import Sidebar from '../components/Sidebar.astro';
import TableOfContents from '../components/TableOfContents.astro';
const { frontmatter, headings, slug } = Astro.props;
// Additional setup code here...
---
<MainLayout title={frontmatter.title}>
<div class="flex flex-col lg:flex-row gap-8 max-w-7xl mx-auto px-4">
<!-- Mobile navigation (collapsed by default) -->
<div class="lg:hidden">
<MobileNav />
</div>
<!-- Sidebar - hidden on mobile -->
<aside class="hidden lg:block w-64 flex-shrink-0">
<div class="sticky top-24">
<Sidebar currentPath={slug} />
</div>
</aside>
<!-- Main content -->
<main class="flex-grow max-w-3xl">
<article class="prose dark:prose-invert prose-lg max-w-none">
<h1>{frontmatter.title}</h1>
<slot />
</article>
</main>
<!-- Table of contents - desktop only -->
<aside class="hidden xl:block w-64 flex-shrink-0">
<div class="sticky top-24">
<TableOfContents headings={headings} />
</div>
</aside>
</div>
</MainLayout>
The real beauty here is how Tailwind makes responsive design so intuitive. I’m not writing media queries – I’m just saying “hide this on small screens” (hidden lg:block
) or “stack these vertically on mobile but put them side by side on desktop” (flex-col lg:flex-row
).
The Search Component That Changed Everything
Documentation without search is like a library without a catalog – technically functional but frustrating to use. After experimenting with several approaches, I built a lightweight client-side search using Astro islands with React:
<!-- src/components/Search.astro -->
---
import SearchComponent from './SearchComponent.jsx';
import { getCollection } from 'astro:content';
// Get all docs for the search index
const allDocs = await getCollection('docs', (doc) => !doc.data.draft);
// Create a simplified search index
const searchIndex = allDocs.map(doc => ({
title: doc.data.title,
description: doc.data.description,
slug: doc.slug,
}));
---
<div class="relative w-full md:w-64">
<SearchComponent client:idle searchIndex={searchIndex} />
</div>
The magic here is in that client:idle
directive. It tells Astro: “Hey, don’t load this component until the browser is idle.” This means the core documentation loads first, and only when that’s done does the search functionality hydrate.
For small to medium-sized documentation sites, this client-side approach works surprisingly well without the complexity of setting up a dedicated search service.
Why I Use TailwindCSS v4 With Pure CSS Configuration
I recently migrated to TailwindCSS v4 for my documentation projects, and it’s been a game-changer. Instead of the JavaScript-based configuration, I now use a pure CSS approach:
/* src/styles/tailwind.css */
@import 'tailwindcss';
@import 'tailwindcss/typography';
@theme {
--font-sans: 'Inter var', ui-sans-serif, system-ui, sans-serif;
--font-mono: 'JetBrains Mono', ui-monospace, monospace;
--color-primary: #4f46e5;
--color-primary-dark: #4338ca;
/* Custom typography settings for docs */
--typography-code: {
font-weight: 400;
background-color: #f1f5f9;
@apply dark:bg-gray-800;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
}
}
/* Custom utility classes for documentation */
@layer utilities {
.prose-custom {
@apply prose-headings:text-gray-900 prose-headings:dark:text-white
prose-code:before:content-none prose-code:after:content-none;
}
}
What I love about this approach is how much more intuitive it feels – it’s just CSS! No more trying to remember the exact nesting structure for the typography plugin configuration.
Performance Tricks That Made a Visible Difference
When I rebuilt that client site I mentioned earlier, a few specific optimizations yielded the biggest performance gains:
- View Transitions for smooth navigation - Astro’s built-in view transitions make page navigation feel instant:
<head>
<ViewTransitions />
</head>
- Image optimization - Using Astro’s built-in image component dramatically reduced load times for screenshot-heavy docs:
<Image
src={screenshot}
alt="UI example"
width={800}
quality={80}
class="rounded-lg shadow-md"
/>
- Partial hydration for interactive elements - I used to load React for the entire site, but with Astro I can do this:
<InteractiveDemo client:visible />
This only loads React when that specific component scrolls into view – a game-changer for performance.
The Real-World Results
I’m not exaggerating when I say the difference was night and day. The client’s documentation site went from:
- 3.2s First Contentful Paint → 0.8s
- 4.7s Time to Interactive → 1.2s
- 1.9MB JavaScript → 87KB (only for search and interactive examples)
But more importantly, users noticed. Support tickets about documentation load times disappeared, and we saw a 24% increase in documentation engagement metrics after the switch.
Is Astro + Tailwind Always the Right Choice?
While I’m obviously a fan, this stack isn’t perfect for every scenario. In my experience:
Great for:
- Documentation sites
- Marketing sites
- Blogs and content-focused websites
- Sites where performance is critical
Perhaps not ideal for:
- Highly interactive applications (though Astro is getting better at this)
- Projects where your team has deep expertise in another framework
- Applications requiring complex client-side state management
What You Should Try Next
If you’re intrigued by what you’ve read here, I’d recommend starting small – maybe converting a documentation section or blog to Astro before committing a whole project.
Here’s what worked well for me as a learning path:
- Build a simple documentation site following their tutorial
- Add TailwindCSS and experiment with styling
- Try implementing a search component with partial hydration
- Explore content collections for organizing your documentation
The Astro documentation itself is a masterclass in how to build great docs, so I highly recommend checking it out for inspiration.
Building documentation sites used to feel like a compromise between features and performance. With Astro and TailwindCSS, I no longer have to choose. I get the developer experience I want along with the performance my users deserve.
If you’ve built something with Astro or have questions about my approach, I’d love to hear from you! Reach out on GitHub or LinkedIn.