Here’s a rewritten version of the article, preserving the original ideas while improving clarity, structure, and flow:
—
Crafting a Better Developer Experience in Astro with TypeScript
One of the things I love most about Astro is its emphasis on developer experience (DX), especially for onboarding new team members. While Astro provides a solid foundation out of the box, it’s still possible to build a complex system that becomes difficult for others (or even future-you) to navigate. That’s something I actively want to avoid.
When working with multiple developers, consistency is key. I want everyone to know exactly what to expect from each component. Ideally, developers should be able to use a component without having to dig through its source code just to understand how it works. Even better, the system should guide them toward best practices and prevent common mistakes.
That’s where TypeScript comes in. Astro ships with TypeScript support by default, and while it’s optional, using it can significantly improve your DX. Let’s walk through how you can use TypeScript to build more intuitive, reliable components in Astro.
Watch the Video
Prefer video? I’ve also created a video walkthrough of this article. You can watch it on YouTube (with chapters and captions) here.
Getting Started
To follow along, start by creating a new Astro project using the minimal template:
npm create astro@latest
Once you’ve got your project set up, remove the default
For styling, I recommend adding Tailwind CSS:
npx astro add tailwind
With that, you’re ready to build your first component.
Building a Reusable Heading Component
Let’s create a flexible component that supports all HTML heading levels (h1–h6), allows custom font sizes and weights, and accepts any additional HTML attributes.
Step 1: Dynamic HTML Tags
Create a new component at ./src/components/Heading.astro:
—
// ./src/components/Heading.astro
const { as } = Astro.props;
const As = as;
—
Astro requires dynamic components to start with a capital letter, so we alias the as prop to As. This allows us to dynamically render any HTML tag.
Now, use the component in your index route:
—
// ./src/pages/index.astro
import Layout from ‘../layouts/Layout.astro’;
import Heading from ‘../components/Heading.astro’;
—
Hello!Hello world
Step 2: Adding Font Size and Weight
Let’s make the component more customizable by adding support for font size and weight. We’ll also set default values and use Tailwind CSS classes.
Update Heading.astro:
—
const weights = {
bold: “font-bold”,
semibold: “font-semibold”,
medium: “font-medium”,
light: “font-light”
};
const sizes = {
“6xl”: “text-6xl”,
“5xl”: “text-5xl”,
“4xl”: “text-4xl”,
“3xl”: “text-3xl”,
“2xl”: “text-2xl”,
xl: “text-xl”,
lg: “text-lg”,
md: “text-md”,
sm: “text-sm”
};
const { as: As = “h2”, weight = “medium”, size = “2xl” } = Astro.props;
—
Now you can use the component like this:
Hello!Hello worldStep 3: Supporting Additional HTML Attributes
To make the component more flexible, we should allow developers to pass in any valid HTML attribute.
Update the props destructuring to capture the rest of the props:
—
const { as: As = “h2”, weight = “medium”, size = “md”, …attrs } = Astro.props;
—
Then spread those attributes onto the element:
This allows developers to pass attributes like id or class directly:
Hello!Hello worldAstro will automatically merge any additional classes with those from class:list—no extra work needed!
Step 4: Adding TypeScript for Safer DX
At this point, the component works great, but