Astro Rocket supports comments at the bottom of blog posts. The feature is pluggable: a single provider switch picks between Giscus (comments stored in a GitHub Discussions thread), Cusdis (a lightweight widget you can use hosted or self-host), and Artalk (a full comment system you run entirely on your own server). Whichever you pick, the wiring underneath is the same careful setup.
It’s off by default. When disabled, no JavaScript and no network requests are added to your blog pages. When enabled, the provider’s script is lazy-loaded — it only downloads when the reader actually scrolls toward the comments section. Readers who never scroll that far pay zero performance cost.
Kept current. This post originally covered only Giscus. Comments are now pluggable: Cusdis was added first (a lightweight, optionally self-hosted widget), and Artalk followed as a fully self-hosted option — contributed by the community in #469 for people who want to own and moderate every comment on infrastructure they control. The setup below reflects the current, three-provider system. If you’re already on Giscus, nothing changed:
providerdefaults to'giscus'.
Three providers, one switch
All three are wired the same way — server-rendered placeholder with reserved height (no layout shift), lazy-loaded on scroll, theme and language that follow the site by default. The difference is where the comments live and how much you run yourself:
| Giscus | Cusdis | Artalk | |
|---|---|---|---|
| Stores comments in | A GitHub Discussions thread | Cusdis (hosted) or your own instance | Your own Artalk server |
| Infrastructure you run | None (GitHub hosts it) | None if hosted; a service if self-hosting | A server + database, always |
| Commenter needs an account? | Yes — a GitHub login | No — name + email, optional | No — name + email |
| You moderate / own the data? | Via GitHub Discussions | Hosted dashboard, or yourself | Entirely yourself, on your server |
| Live light/dark sync | Yes (postMessage) |
No — re-renders on toggle | Yes (setDarkMode) |
| Reactions | 👍 ❤️ 🎉 etc. | — | Voting + emoji |
| Best for | Dev-audience blogs already on GitHub | General audiences, minimal setup | Owning and self-hosting everything |
- Pick Giscus if your readers are developers who already have GitHub accounts and you’re happy storing the thread in a public repo — there’s no server to run.
- Pick Cusdis if you’d rather not gate commenting behind a GitHub login and want the lightest setup — use the hosted service, or self-host later if you outgrow it.
- Pick Artalk if you want to own and moderate everything — comment data, users, and moderation — on infrastructure you control, and you’re comfortable running a small service. Unlike Giscus and Cusdis, Artalk has no hosted option: you supply the server.
Step 1 — Set up your provider
If you choose Giscus
- Go to giscus.app.
- Pick the GitHub repo you want to host the discussions in. The repo must be public and have Discussions enabled (Settings → Features → Discussions).
- Install the Giscus app on that repo.
- Choose the discussion category (most people use “General” or create a dedicated “Comments” category).
- Copy the four values shown on the page:
data-repo,data-repo-id,data-category, anddata-category-id.
If you choose Cusdis
- Sign up at cusdis.com — or self-host it (Docker, Vercel, etc.) if you want to own the data.
- Create a website in the dashboard.
- Copy the App ID from the embed code it gives you (a UUID).
- Self-hosting? Note your instance URL — you’ll set it as
hostso the widget and its API point at your server instead ofcusdis.com.
If you choose Artalk
Artalk is fully self-hosted — there’s no hosted service, so you run the backend yourself (see the Artalk docs; it ships as a single Go binary or a Docker image and stores comments in SQLite/MySQL/PostgreSQL).
- Deploy an Artalk server and note its public address. In production this must be an
https://URL — a plainhttp://script is blocked as mixed content on an https site and is open to tampering. - In the Artalk dashboard, create a site and note its site name (Artalk uses it for multi-site isolation).
- You’ll set the address as
serverand the name assite. That’s all Artalk needs; the client JS/CSS are served from your Artalk server by default.
Step 2 — Configure Astro Rocket
Open src/config/site.config.ts and find articleFeatures.comments. Flip enabled to true, set provider, and fill in that provider’s block:
articleFeatures: {
comments: {
enabled: true,
provider: 'giscus', // 'giscus' | 'cusdis' | 'artalk'
giscus: {
repo: 'you/your-repo',
repoId: 'R_kgDOAbcdef',
category: 'General',
categoryId: 'DIC_kwDOAbcdef',
mapping: 'pathname',
reactionsEnabled: true,
theme: '', // empty → follow the site's light/dark mode
lang: '', // empty → follow the site's current locale
},
cusdis: {
appId: '00000000-0000-0000-0000-000000000000',
host: 'https://cusdis.com', // your URL when self-hosting
theme: '', // empty → follow the site's light/dark mode
lang: '', // empty → follow the site's current locale
},
artalk: {
server: 'https://comments.example.com', // your Artalk server (https in prod)
site: 'My Blog', // the site name you created in Artalk
// jsUrl / cssUrl: optional — override to serve the client from a CDN
// darkMode: leave unset → follow the site's light/dark mode (live)
// locale: leave unset → follow the site's current locale
},
},
},
Only the block matching provider needs real values — leave the others as-is. Build, deploy, and every blog post now has a comments section underneath.
The component fails soft: if the selected provider isn’t fully configured (Giscus missing any of its four IDs, Cusdis missing its App ID, or Artalk missing its server or site), it renders nothing rather than breaking the page. A half-finished enabled: true just stays invisible until you complete setup.
Theme and language follow the site
Leave the theme and language fields empty (the default) and the embed mirrors your site automatically:
- Theme follows the visitor’s live light/dark choice, resolved from the same colour-mode system the rest of the site uses. Giscus and Artalk both update instantly when the visitor toggles — Giscus via its
postMessageAPI, Artalk via itssetDarkModemethod. Cusdis has no live theme API, so on a toggle its thread is re-rendered — a brief reload. To opt out of site-following on Cusdis, set itsthemeto'auto'(OS preference),'light', or'dark'; on Artalk, setdarkModeto'auto',true, orfalse. - Language follows the page’s current locale, so a
/nl/blog/...post loads the comment widget in Dutch with no extra config.
Set the relevant field to a specific value to override the site-following behaviour.
Per-post override
Some posts don’t need comments — announcements, legal pages, or quick updates. Hide comments on a single post with frontmatter:
---
title: My announcement
comments: false
---
The site-wide setting stays on; just this post skips the comments block.
How the lazy-loading works
If the comment script were loaded on every article page-load, it would add a third-party script (and, for Giscus, an iframe) to every blog post — roughly 100 KB in Giscus’s case. That hurts Lighthouse scores and feels especially wasteful for readers who only skim the first paragraph.
So the component does this instead, regardless of provider:
- On the server, it renders an empty
<div>with a reservedmin-height: 360px(so there’s no layout shift when the widget later appears). - On the client, an
IntersectionObserverwatches that<div>. - When the reader scrolls within
300pxof the comments area, the provider’s script is appended to the DOM and the widget loads.
Readers who bounce or skim → 0 KB. Readers who scroll to the bottom → the comments are ready by the time they arrive. To make that first request even faster, BaseLayout adds a <link rel="preconnect"> to the active provider’s host when comments are enabled, warming the DNS+TLS handshake before the script fires. (For Artalk, that’s your server’s origin.)
What it costs
| Scenario | Cost |
|---|---|
| Comments disabled (default) | 0 KB, 0 requests |
| Enabled, reader doesn’t scroll to comments | ~0.5 KB inline JS |
| Enabled, reader scrolls to comments | Giscus ~100 KB; Cusdis much less; Artalk served from your own server — all async, below-the-fold, no LCP impact |
The reserved min-height prevents CLS. The async load prevents render blocking. The IntersectionObserver prevents wasted requests. And because the comments component is a thin dispatcher, only the provider you selected ships its client script — the others are never bundled.
Where it lives
- Dispatcher:
src/components/blog/Comments.astro(renders the selected provider) - Providers:
src/components/blog/CommentsGiscus.astro,CommentsCusdis.astro, andCommentsArtalk.astro - Wiring:
src/layouts/BlogLayout.astro - Config:
src/config/site.config.ts→articleFeatures.comments - Per-post override:
comments: falsein MDX frontmatter
For the full tour of site.config.ts, see the configuration guide.
That’s the entire feature. Pick a provider, paste a few values into your config, and your blog has a real comment thread — without a performance hit, and (with Giscus or hosted Cusdis) without a database to run yourself.