Skip to main content
Cover image for Building Static Sites with Astro & TailwindCSS

Building Static Sites with Astro & TailwindCSS

astro tailwindcss daisyui guide webdev

I wanted to build a portfolio site that was fast, simple, and focused on content. Every time I tried React or Vue, I found myself obsessing over bundle sizes, hydration strategies, and client-side state management for what was essentially static content. Astro solved this by shipping zero JavaScript by default.

The development experience is what won me over. No build step configuration, no hydration debugging, no explaining to other developers why we need a complex framework for a blog. Just write components, ship HTML, and move on.

Why Astro Works for Content Sites

The core philosophy is straightforward: render everything on the server, ship zero JavaScript to the client unless you explicitly need it. This prevents the performance problems I’d encountered with other frameworks—hydration delays, unnecessary bundle splits, and the constant temptation to add interactive features just because the framework makes it easy.

Components Feel Familiar

Astro components are essentially HTML files with superpowers. You get TypeScript, imports, and component composition, but there’s no JSX or virtual DOM to learn. If you’ve written web development before, you already understand Astro.

Here’s a simple example. This Divider component imports an image in the frontmatter and renders standard HTML:

// src/components/Divider.astro
---
const { Class } = Astro.props;
import { Image } from "astro:assets";
import sadKirblet from "../images/svg/sad-kirblet.svg";
---

<div class="{`${Class}" divider pb-5`}">
  <image src="{sadKirblet}" alt="Kirblet!" class="w-16" />
</div>

Components can be imported and used directly in MDX files.

Kirblet!

Layouts Keep Things Organized

I used to copy-paste headers and footers across every page. Astro’s layout system solved that. A single BaseLayout.astro handles navigation, theming, and responsive structure. Each page focuses on its own content.

The best part: layouts nest. I created a PostLayout.astro for blog posts that extends BaseLayout.astro. The post layout adds article-specific markup while inheriting the site-wide navigation and footer. Changes to the base layout automatically propagate everywhere.

Type-Safe Content Without Runtime Overhead

I wanted blog posts to be validated at build time but didn’t want to pay a runtime performance cost. Content Collections gave me exactly that. Define a Zod schema once, and Astro validates every frontmatter during the build.

src/content/
├── config.ts
├── posts
   ├── 1-Astro.mdx
└── projects
    └── 1-Capsule.mdx
// config.ts
const postsCollection = defineCollection({
  type: "content",
  schema: ({ image }) => z.object({
    title: z.string(),
    pubDate: z.string(),
    description: z.string(),
    author: z.string(),
    cover: image(),
  }),
});

This caught typos and missing fields before deployment. I learned this the hard way when I published a post with a missing publication date—Content Collections would have flagged it instantly.

Dynamic routes use the getCollection function to retrieve content, and the <Content /> component handles rendering. Zero runtime overhead, full type safety.

Styling with Tailwind and DaisyUI

I chose TailwindCSS for styling because it keeps design decisions visible in the markup. DaisyUI added semantic components on top, reducing repetitive class names. The configuration stayed minimal:

// tailwind.config.cjs
module.exports = {
  content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
  plugins: [require("@tailwindcss/typography"), require("daisyui")],
  daisyui: {
    themes: ["luxury", "dim"],
  },
  theme: {
    extend: {
      screens: {
        xl14: "1440px",
        xl2k: "2048px",
      },
    },
  },
};

Site-wide consistency without writing custom CSS files.

When Astro Makes Sense

This stack prioritizes simplicity. It produces static files for CDN deployment—no server-side rendering, no edge functions, no real-time capabilities. These constraints prevent over-engineering and keep the focus on content.

The result: a site that loads instantly, costs nothing to host, and works with JavaScript disabled. Astro’s build process handles optimization automatically. I don’t think about bundle splitting or code elimination.

Astro excels for portfolios, blogs, and documentation. For highly interactive applications, I’d use something else. But for content-focused sites where performance and simplicity matter, Astro hits the sweet spot.