Limited offer: $250 off for the first 200 customers!Claim discount

Blog Components

Small presentational components that make up the blog index and post pages.

Where posts come from

All blog posts are defined in src/blog/_assets/content.ts as entries in the articles array. Each entry is a plain TypeScript object — no MDX, no CMS, no database required. The content field is an HTML string styled automatically by @tailwindcss/typography.

// src/blog/_assets/content.ts
{
  slug: "my-first-post",
  title: "My First Post",
  description: "A short summary shown on the card.",
  date: "2026-06-01",
  cover: "/blog/cover.png",
  author: {
    name: "Your Name",
    role: "Founder",
    avatar: "/portrait.jpg",
  },
  tags: ["launch", "saas"],
  content: `<p>Your post content as HTML.</p>`,
}
  • slug — used as the URL: /blog/my-first-post. Must be URL-safe (lowercase, hyphens, no spaces).
  • title — displayed as the card heading and page title.
  • description — the excerpt shown on the card and used as the meta description.
  • date — ISO 8601 date string. Controls display order (newest first) and the formatted date in PostMeta.
  • cover — optional image path relative to /public. Falls back to a gradient placeholder if omitted.
  • author.name — shown in PostMeta. Also used as the avatar fallback initial.
  • author.role — optional role/title shown alongside the name.
  • author.avatar — optional image URL. If omitted, a circle with the author's first initial is shown.
  • tags — array of strings rendered as CategoryTag pills on the card.
  • content — raw HTML for the post body. Use <p>, <h2>, <ul>, <li>, <strong>, <a>, <code> etc. Tailwind Typography handles all the styling.

CardArticle

Renders a blog post preview card with a 16:9 cover image (or gradient fallback), the PostMeta row, the post title as a link, the description excerpt, and CategoryTag pills for each tag. Used on the /blog index page.

import CardArticle from "@/components/blog/CardArticle";

<CardArticle post={post} />

The only prop is post — a BlogPost object with the same shape as the entries in content.ts. The card resolves the cover image, author, tags, and link internally.

CategoryTag

A small rounded pill that displays a tag name. Non-interactive — it's a presentational <span>. Rendered inside CardArticle for each string in post.tags.

import CategoryTag from "@/components/blog/CategoryTag";

<CategoryTag name="launch" />

PostMeta

Renders the author avatar, author name, and formatted publish date in a single row. Uses Avatar internally. Shown at the top of both the card and the full post page.

import PostMeta from "@/components/blog/PostMeta";

<PostMeta post={post} />

Avatar

A circular avatar. If author.avatar is set it renders the image; otherwise it shows a circle with the first letter of author.name as a fallback. Used inside PostMeta — you rarely need to use it directly.

import Avatar from "@/components/blog/Avatar";

<Avatar author={{ name: "Jane", avatar: "/jane.jpg" }} size={36} />
  • author — an object with name (required) and optional avatar (image URL).
  • size — pixel size for both width and height. Defaults to 36.
TipTo add a new post, append a new object to the articles array in src/blog/_assets/content.ts. No routes, no files, no CMS — the slug becomes the URL automatically via the /blog/[slug] dynamic route.
TipPlace cover images in /public/blog/ and reference them as "/blog/my-cover.png". Keep them at 1200×675 px (16:9) for the best appearance on the card and when shared on social media.