Stedefast

Modules

Comments

3 min read

The comments module adds a comment thread to any page. Comments are stored in Cloudflare D1, moderated through the admin panel, and pre-rendered to static JSON at build time.

Installation

pnpm add @stedefast/module-comments @stedefast/provider-cloudflare

Setup

// stedefast.config.ts
import { defineConfig } from "@stedefast/core";
import { CommentsModule } from "@stedefast/module-comments";

export default defineConfig({
  // ...
  modules: [
    CommentsModule({
      requireApproval: true,  // default: true — comments need admin approval
    }),
  ],
  cloudflare: {
    projectName: "my-site",
    d1Databases: [{ binding: "DB", databaseId: "YOUR_D1_DATABASE_ID" }],
  },
});

Cloudflare bindings

The comments module requires a D1 database. Create one with Wrangler:

wrangler d1 create my-site-db

Copy the database_id from the output into your config. Then apply the migrations:

wrangler d1 migrations apply my-site-db --local   # for dev
wrangler d1 migrations apply my-site-db           # for production

Environment variables

Set these in your .dev.vars for local development and as Cloudflare Pages secrets for production:

Variable Description
BETTER_AUTH_SECRET Secret used to verify admin requests to the moderation endpoint
COMMENTS_REQUIRE_APPROVAL "true" (default) or "false" — skip moderation queue

Using the island in a template

The comments island mounts automatically on any page where the module is registered. Add the mount point in your template:

// theme/templates/post.tsx
<div
  data-island="CommentsIsland"
  data-props={JSON.stringify({ pageId: page.slug })}
/>

The island loads the pre-built comments JSON, renders the thread immediately, and provides a submission form with markdown preview.

How it works

  1. Build timebuildStaticExport() queries D1 for all approved comments per page and writes dist/data/comments/<slug>.json
  2. Page load — The island fetches /data/comments/<slug>.json and renders immediately from CDN
  3. SubmitPOST /_modules/comments/submit inserts to D1; the comment is marked pending
  4. Moderate — An admin approves the comment in the admin panel; the D1 row is updated to approved
  5. Rebuild — On next stedefast build (or via a deploy hook), the static JSON is regenerated

Moderation

Pending comments are visible in the admin panel at /admin/comments. From there you can approve, reject, or bulk-action the queue.

Typed hooks

The comments module exports typed fetch functions and React hooks from its /hooks subpath. These are what the built-in island uses internally, and what ejected islands import after running stedefast scaffold module-island comments.

import { loadComments, postComment, useComments } from "@stedefast/module-comments/hooks";
import type { Comment, CommentsIslandProps, SubmitCommentBody } from "@stedefast/module-comments";
Export What it does
loadComments(pageId) Fetches /data/comments/{pageId}.json — returns Comment[]
postComment(body) POSTs to /_modules/comments/submit — throws on error
useComments(pageId, initial?) React hook wrapping loadComments — returns { comments, loading, error }
moderateComment(body, token) POSTs to /_modules/comments/moderate — for admin use

Because postComment is typed as (body: SubmitCommentBody) => Promise<SubmitCommentResponse>, if the API changes in a future module version the TypeScript compiler will catch the mismatch in any code that calls it.

Configuration reference

Option Type Default Description
requireApproval boolean true If false, comments are published immediately without moderation