I've been working on prototyping for the past few days and I figured I'd stop to write up a milestone, even if it was sort of minor. My goal was to set up a website to house prototypes and demos. It's live now1 at https://prototypes.likes.fyi and as usual the code is available on github and tangled. Read on for a quick tour of the interesting parts of getting an Astro website up on Cloudflare.

Astro, Theme, Hosting

The first step is to get a basic website up and running. I'm using Astro and Tailwind because they're what I know, not because they're necessarily the best tools for the job.

I dug around in Astro's theme gallery and found miniblog (github) which seemed like a good place to start. Astro doesn't have support for modular themes so you're supposed to clone the theme's repo and build on top of it. I don't like that so I did a bunch of extra work instead:

$ git init
$ git commit --allow-empty -m 'git init'
$ git add LICENSE README.md
$ git commit -m 'add license and readme'
$ git clone https://github.com/nicholasdly/miniblog
$ rm -rf miniblog/.git miniblog/README.md miniblog/LICENSE
$ mv miniblog/* .
$ mv miniblog/.* .
$ rmdir miniblog
$ git add .
$ git commit -m 'import nicholasdly/miniblog@65ba328'

Could you have cloned the template repo, removed its .git directory, rewritten its README.md, and accomplished essentially the same task? Yes. Would that be easier, cleaner, and safer than my method? Also yes. Is it what I did? No.

Next up was some customization of the theme. Nothing particularly interesting here, just tailoring it to my needs. Take a look at the the commit history if you're curious.

Finally, hosting. I'm deploying on Cloudflare using their instructions for Astro + Cloudflare Pages. Nothing interesting here either, just followed that guide exactly and used the Cloudflare dashboard to point prototypes.likes.fyi at it.

Now that we have a basic shell of a website, let's get some content into it.

Content Collections

Astro has a feature called Content Collections which allows you to flexibly load data into your system for further processing. A common use case would be a directory full of blog posts written in markdown that get parsed and used to generate markup for each post, index pages, tag pages, etc.

To make this work you use a Loader to source your data. My content is just2 files on disk so I picked the built-in glob loader. I want to keep my prototypes in src/content either as single files or as dated subdirectories, e.g. src/content/2026-04-29. I want to find any .md/.mdx file in src/content but I only want to find index.{md,mdx} files in the directories, and only one level deep. Here's the loader config I'm using:

loader: glob({ base: "./src/content", pattern: [
  "*/index.{md,mdx}",
  "*.{md,mdx}",
]})

Content Collections also lets you validate content against a schema written in Zod. I'll make better use of this later but let's at least get the basics in place now:

schema: z.object({
  title: z.string(),
  subtitle: z.string(),
})

Putting it all together, here's the full src/content.config.ts:

import { defineCollection } from "astro:content";
import { glob } from "astro/loaders";
import { z } from "astro/zod";

const pages = defineCollection({
  loader: glob({ base: "./src/content", pattern: [
    "*/index.{md,mdx}",
    "*.{md,mdx}",
  ]}),

  schema: z.object({
    title: z.string(),
    subtitle: z.string(),
  })
});

export const collections = { pages };

That's the input side of Content Collections taken care of. The output side is pretty much by the book, nothing particularly interesting here.

Other Stuff

Another few small things:

That's it for now. Next time I'll be adding the first prototype to the site. Thanks for reading!