Web components in MDX

Dropping interactive web components into blog posts — one home-made, one imported.

Most of this site ships zero JavaScript — Astro renders everything to static HTML. But when a post needs interactivity, web components are a perfect fit: they’re just HTML tags with behavior attached, no framework runtime required. Here are two in action.

A home-made component: <star-rating>

This one is defined in about seventy lines of vanilla TypeScript in src/scripts/star-rating.ts — shadow DOM, keyboard-accessible buttons, and a custom event when the rating changes. Click a star:

Using it in a post is one import and one tag:

import StarRating from '../../components/StarRating.astro';

<StarRating value={3} />

An imported component: <lite-youtube>

lite-youtube by Justin Ribeiro renders a YouTube embed as a static thumbnail and only loads the real iframe (and its ~1 MB of YouTube JavaScript) when you press play. Same one-tag usage:

import YouTube from '../../components/YouTube.astro';

<YouTube videoid="dQw4w9WgXcQ" videotitle="A very important video" />

Why wrap them in .astro components?

Both tags are wrapped in tiny Astro components (src/components/) whose <script> block imports the element definition. Astro bundles and dedupes those scripts automatically — use ten ratings on a page and the definition still loads once. The pattern for adding any future web component is the same three steps: install (or write) it, wrap it, import it in your post.