How to grow mlsystems.dev as needs evolve — auth, paid courses, interactive learning, comments, search, newsletter.
The codebase is intentionally modular. Each feature plugs in without refactoring the rest.
Astro supports both static and SSR modes. To enable auth (signup, login, sessions):
-
Add an SSR adapter:
npx astro add vercel # or: npx astro add node # or: npx astro add netlify # or: npx astro add cloudflare
-
In
astro.config.mjs, changeoutput: 'static'tooutput: 'hybrid'. Hybrid means static by default, SSR opt-in per page. -
Pick an auth library:
- Lucia — framework-agnostic, simple, you own the session table
- Auth.js (formerly NextAuth) — supports many OAuth providers out of the box
- Better Auth — modern, type-safe, lightweight
- Clerk — managed service, beautiful UI, has an Astro SDK
- Supabase Auth — pairs well with Supabase as your DB
-
Mark dynamic pages as SSR:
--- export const prerender = false; // this page renders on every request ---
Everything else stays static. Your blog pages don't lose any speed.
-
Add a
coursescollection insrc/content/config.ts:const courses = defineCollection({ type: 'content', schema: z.object({ title: z.string(), summary: z.string(), lessons: z.array(z.string()), price: z.number().optional(), // ... etc }), });
-
Create
src/content/courses/and add course MDX files. -
Add a course route:
src/pages/courses/[slug].astro. -
Wire payments via Stripe Checkout — entitlements stored in your DB.
-
Gate course content behind auth: check session in the page's frontmatter, redirect to login or show a preview.
- Build modules as React islands in
src/components/learn/. - Hydrate with
client:visibleso they don't block first paint. - Persist progress via API routes → DB (once auth is in place).
For inspiration, see how Playground.tsx is structured — it's a self-contained interactive React component that mounts on the playground page.
Currently Comments.tsx is an in-memory placeholder. The easiest path to real comments:
Giscus uses GitHub Discussions as the backing store. Free, no DB, no backend, perfect for a dev audience.
- Enable Discussions on your GitHub repo
- Install the Giscus app on the repo
- Configure at giscus.app — get a script snippet
- Replace
Comments.tsxwith a Giscus React wrapper, or drop the script directly into the article layout - Configure
term={postSlug}so each post maps to its own thread
Comments live in your GitHub Discussions tab. You own the data forever. Moderation uses normal GitHub tools.
Once auth is in place, you can build a custom comment system with API routes and a DB. More work, more control. Probably not worth it before you have real traffic.
Pick a service and drop in their embed. No backend code needed:
- Buttondown (recommended) — dev-first, RSS-to-email auto-sends new posts, free up to 100 subs
- Resend Audiences — modern API, free 3,000 emails/month
- EmailOctopus — cheap and simple, free 2,500 subs
Add the embed form to the homepage CTA and footer.
Add Pagefind for a fully static, client-side search index:
npm install pagefind- Add Pagefind to your build script (it runs after
astro build) - Drop the Pagefind UI component into a search page or modal
Pagefind builds the index at build time and ships a tiny WASM runtime to the browser. Zero infrastructure, sub-100ms search results, works offline.
The THREADS array in src/lib/data.ts is hardcoded sample data. When you're ready for a real forum:
- Self-host Discourse — the industry standard for dev communities
- Linen.dev — Slack/Discord mirror with SEO, hosted
- Just use GitHub Discussions — already free, already integrated with Giscus
For each, iframe under /community or build a thin reader against the forum's API.
Currently the homepage shows aspirational numbers like "284 articles · 47 contributors." These are placeholders in lib/data.ts.
To wire to real counts:
---
import { getCollection } from 'astro:content';
const posts = await getCollection('posts', ({ data }) => !data.draft);
const articleCount = posts.length;
const contributorCount = new Set(posts.map((p) => p.data.authorHandle)).size;
---Drop articleCount and contributorCount into the JSX where the placeholders currently live.
- Newsletter analytics — track open rates via Buttondown's built-in dashboard
- OG image generation — auto-generate per-post OG cards with
@vercel/ogat build time - Reading progress bar — small React island,
client:idle - Dark mode toggle — design tokens already include dark variants in
global.css; add a toggle - Author pages — promote
authorfrom a string to a content collection with bios, social links, post listings - Multiple authors per post — schema change:
authors: z.array(z.string()) - Citation export — "cite this article" button generating BibTeX / APA / etc.
- Embedded notebooks — Pyodide for in-browser Python, perfect for tokenizer/attention demos