Skip to content

Publishing a Blog Post

Available Roles: Docs contributor, Marketing Platforms: Web Reviewed: 2026-08-12

Use this page when you need to add a post to the Hyper blog or take an existing draft live. It covers where post files live, which frontmatter fields are required, how drafts behave in each environment, and what to check before you merge.

Blog posts are Markdown files in the website repository. There is no CMS and no publishing UI — a post goes live when a commit containing it deploys to production.

  • You are writing a new post and need the file location, frontmatter, and review steps.
  • You have a draft in the repository and want to publish it.
  • You need to correct a live post, or add an updated date to one.
  • Clone the website repository and install dependencies with bun install.
  • Decide the byline. The author field prints verbatim on the post and the index card.
  • Decide whether the post ships as a draft first. Drafts are visible locally and excluded from production, so a draft can be merged safely before it is ready to announce.
  • Have the description written. It is used as the meta description, the social share text, and the card summary, so it needs to work as a standalone sentence.
Path Purpose
src/content/blog/<slug>.md The post itself. The filename becomes the URL slug.
src/content.config.ts The blog collection schema. Frontmatter is validated against it at build time.
src/lib/blog.ts Shared helpers: post ordering, draft filtering, reading time, tag lookups.
src/lib/tag-slug.ts Tag normalization and aggregation, shared with the collection schema.
src/pages/blog/index.astro Post index, topic list, and empty state.
src/components/blog/PostListItem.astro A single row in the post list, used by the index and tag pages.
src/pages/blog/[slug].astro Individual post route, Open Graph metadata, and structured data.
src/pages/blog/tags/[tag].astro One page per tag, generated from the tags in use.
src/pages/blog/rss.xml.ts The RSS feed at /blog/rss.xml.

The filename is the URL. src/content/blog/on-device-inference.md publishes to /blog/on-device-inference. Use lowercase words separated by hyphens, and do not rename a file after it has been live — that breaks inbound links and the feed.

Field Required Notes
title Yes 70 characters or fewer. Sentence-style capitalization, no trailing period.
description Yes 160 characters or fewer. One complete sentence.
pubDate Yes YYYY-MM-DD. Controls sort order on the index and in the feed.
updatedDate No YYYY-MM-DD. Adds an “Updated” line to the post header.
draft No Defaults to false. Set true to keep a post out of production.
tags No Array of strings. Each tag used by a published post generates a tag page, so a tag must contain at least one letter or number. Spellings that normalize to the same slug share one page.
author No Defaults to Hyper Team.
authorRole No Shown under the byline in the post list, e.g. Co-founder. Omitted if unset.
authorImage No Path to a headshot in src/assets, relative to the post file. Falls back to an initials monogram.
ogImage No Absolute path or URL for the social share image. Falls back to the site default.

The build fails on a schema violation rather than shipping a broken post, so a title over 70 characters or a missing pubDate is caught by bun run build.

---
title: "Introducing Hyper"
description: "Why we're building a meeting platform where the AI runs on your device."
pubDate: 2026-08-12
draft: true
tags: ["announcement", "product"]
author: "Zach Sherman"
---
  1. Create the file at src/content/blog/<slug>.md.
  2. Add the frontmatter block. Start with draft: true unless the post is already reviewed and ready.
  3. Write the body in Markdown, starting with a paragraph rather than a heading. The title field renders the page’s only h1, so a # heading in the body would produce a second one.
  4. Use ## for sections and ### for subsections. Headings at those two levels build the “On this page” sidebar, which appears once a post has more than one section.
  5. Keep the first paragraph strong. It renders at a larger size as the post’s lead, and it is the text most readers see before deciding to continue.
  6. Link internally with absolute paths such as /docs/getting-started/overview or /pricing. Link to the waitlist with /#waitlist.

Reading time is calculated from the body at build time. Do not write it into the post.

Author headshots go in src/assets and are referenced from authorImage as a path relative to the post file, such as ../../assets/authors/zach.jpg. The collection schema validates the file exists and Astro optimizes it, so a missing or misspelled path fails the build instead of rendering a broken image. Posts without a headshot show an initials monogram, which is the intended default rather than a placeholder to fill in.

Put other post images in src/assets and reference them through Astro’s asset pipeline rather than dropping files in public/. Give every image explicit dimensions so the page does not shift while it loads, and follow the image workflow in the repository’s AGENTS.md. Use public/ only when an asset needs a fixed, permanent URL — for example an ogImage you intend to share directly.

Start the dev server and open /blog:

Terminal window
bun run dev

Both servers run in the background under coding agents, each with its own subcommands: astro dev status, astro dev logs, and astro dev stop for the dev server, and astro preview status, astro preview logs, and astro preview stop for the preview server.

Check the post at three sizes — mobile, tablet, and desktop — and check both themes with the toggle in the header. The blog inherits the site’s dark and light palettes, so a post that looks correct in one theme can still fail in the other, most often in code blocks and tables.

Environment Drafts visible Notes
bun run dev Yes Drafts render with a Draft badge on the card and in the post header.
bun run build and production No Drafts are excluded from the index, tag pages, the feed, and routing entirely.

Because drafts are excluded from routing, a production build generates no page for a draft post. This applies to every environment built with astro build — including bun run preview and the staging deployment — so a draft URL returns a 404 there too, not only in production. Review drafts locally with bun run dev, which is the only place they render.

  1. Remove draft: true, or set it to false.
  2. Set pubDate to the intended publish date. If the post sat in review, update it — this is the date readers see.
  3. Run the verification commands below.
  4. Open a pull request with a Conventional Commits title, such as docs(blog): add on-device inference post.
  5. Merge and deploy. The post, the tag pages, the RSS feed, and the sitemap all regenerate from the same build.
Terminal window
bun run check
bun run build

astro check catches type and template errors. bun run build validates frontmatter against the collection schema and confirms the post, its tag pages, and the feed all generate.

After the build, confirm:

  • The post appears at /blog/<slug> with the correct date, byline, and reading time.
  • The post is listed on /blog, newest first.
  • Each tag links to a tag page that lists the post.
  • /blog/rss.xml includes the post.
  • Tables and code blocks are readable in both themes and scroll rather than overflow on mobile.
  • Title is 70 characters or fewer and reads as a specific claim, not a category.
  • Description stands alone as one sentence and is 160 characters or fewer.
  • First paragraph says what the post is about without requiring the title as setup.
  • Product claims match what Hyper currently does. Availability language matches the docs.
  • No invented metrics, customer names, or dates.
  • Internal links resolve, and external links use https.
  • Tags reuse existing tags where one fits, rather than creating a near-duplicate.
Symptom Cause Fix
Build fails with a Zod or collection error naming the post Frontmatter violates the schema in src/content.config.ts Read the field name in the error. Most often a title over 70 characters, a description over 160, or a malformed pubDate.
Post is missing from /blog after deploy draft is still true Set draft: false or remove the field, then rebuild.
Post is at the wrong position on the index pubDate is older than expected The index sorts by pubDate descending. Correct the date.
Two h1 headings on the page The body starts with a # heading Remove it. title already renders the h1.
“On this page” sidebar is missing The post has one section or none Expected. The sidebar appears only when a post has more than one ## or ### heading.
Tag page 404s The tag was changed or removed from every post Tag pages are generated from tags in use. Restore the tag or update the inbound link.
Share preview shows the default site image No ogImage set Expected fallback. Add ogImage to use a post-specific image.