I wanted comments on my Astro blog without taking on a database, moderation panel, or another service to babysit, so I ended up using Giscus. It stores discussions in GitHub, takes about 10 minutes to wire up, and with a little extra code it can follow your site’s dark mode cleanly.
This is the same setup running on this site, scroll to the bottom of this post to see it live.
What You Need
- An Astro site with a layout you can add a component to
- A public GitHub repo, your site’s repo works fine
- Permission to install a GitHub app on that repo
One thing to know before you commit to Giscus: readers need a GitHub account to comment. For a dev blog that’s basically everyone, but for a general audience blog it might rule it out.
Why I Picked Giscus
I’ve wanted comments on the blog for a while, mostly so posts could have a place for follow-up questions and corrections. I wanted something I didn’t have to host, something easy to moderate, and something I could make match the rest of the site. I landed on Giscus after seeing it on Nick Winans’s blog, and it checked all of those boxes.
The short version on alternatives: Disqus was heavier than I wanted, Utterances was close but less flexible, and self-hosted options felt like too much infrastructure for a static site. Giscus gave me threaded replies, reactions, GitHub-based moderation, and no server to maintain.
Adding Giscus
The initial setup all happens on the Giscus site, which generates a script tag configured for your repo.
- Go to Giscus.app
- Create a public repo that you want to use, enable the Discussions feature in settings. This is disabled by default and Giscus won’t work without it
- Install the Giscus app, you can select it to manage just the repo you’ve created
- Select the mapping (I use
pathnamewhich ties each discussion thread to the page’s url path), themes, and any other options there
- Copy the
<script>that Giscus generates for you - Add it to your article page in Astro.
- If you want it somewhere else you can create a div with class giscus
- ie:
<div class="giscus pt-2"></div>
- ie:
At this point comments work. The script injects an iframe, and the first comment on any page automatically creates a matching Discussion thread in your repo.
Stylizing Giscus
You can either use one of the existing themes or pass in your own fully qualified url path to a custom css file into the data-theme attribute as is documented in giscus’s advanced usage docs
The main extra step is theme syncing. Giscus lives in an iframe, so you can’t just restyle it with CSS when your site theme changes. You need to send it a postMessage when the theme flips, and you want to set the initial theme before the script loads so it doesn’t flash the wrong colors on first render.
<script is:inline>
function updateGiscusTheme() {
const dataTheme =
document.documentElement.getAttribute("data-theme") || "light";
const giscusFrame = document.querySelector("iframe.giscus-frame");
if (!giscusFrame) return;
giscusFrame.contentWindow.postMessage(
{
giscus: {
setConfig: {
theme: dataTheme === "dark" ? "dark_dimmed" : "light",
},
},
},
"https://giscus.app",
);
}
document.addEventListener("themechange", updateGiscusTheme);
</script>
<div class="giscus"></div>
<script>
// doing this manually lets astro pick up the initial set theme without flashing or erroring
const theme =
localStorage.getItem("theme") === "dark" ? "dark_dimmed" : "light";
const script = document.createElement("script");
script.src = "https://giscus.app/client.js";
script.async = true;
script.crossOrigin = "anonymous";
script.setAttribute("data-repo", "davidteather/dteather.com");
script.setAttribute("data-repo-id", "R_kgDONz7hYA");
script.setAttribute("data-category", "Announcements");
script.setAttribute("data-category-id", "DIC_kwDONz7hYM4CmoaD");
script.setAttribute("data-mapping", "pathname");
script.setAttribute("data-reactions-enabled", "1");
script.setAttribute("data-emit-metadata", "1");
script.setAttribute("data-input-position", "top");
script.setAttribute("data-lang", "en");
script.setAttribute("data-loading", "lazy");
script.setAttribute("data-theme", theme);
document.querySelector(".giscus")?.appendChild(script);
</script>Make sure to swap the data-repo, data-repo-id, data-category, and data-category-id values for the ones the Giscus configurator generated for your repo, mine won’t work on your site. The rest of the attributes map to the options you picked on giscus.app, so you can tweak things like data-input-position (comment box above or below the thread) here without regenerating anything.
If you want a tighter visual match with your Astro theme, the next step would be generating a custom Giscus CSS theme from your site colors and passing that stylesheet through data-theme.
Complete GiscusComments.astro Example
If you want this wrapped in one component instead of spreading it across a layout file, a dedicated component works well too:
---
const theme =
Astro.cookies.get("theme")?.value === "dark" ? "dark_dimmed" : "light";
---
<div class="giscus"></div>
<script is:inline>
function updateGiscusTheme() {
const dataTheme =
document.documentElement.getAttribute("data-theme") || "light";
const giscusFrame = document.querySelector("iframe.giscus-frame");
if (!giscusFrame) return;
giscusFrame.contentWindow.postMessage(
{
giscus: {
setConfig: {
theme: dataTheme === "dark" ? "dark_dimmed" : "light",
},
},
},
"https://giscus.app",
);
}
document.addEventListener("themechange", updateGiscusTheme);
</script>
<script define:vars={{ theme }}>
const script = document.createElement("script");
script.src = "https://giscus.app/client.js";
script.async = true;
script.crossOrigin = "anonymous";
script.setAttribute("data-repo", "your-org/your-repo");
script.setAttribute("data-repo-id", "YOUR_REPO_ID");
script.setAttribute("data-category", "General");
script.setAttribute("data-category-id", "YOUR_CATEGORY_ID");
script.setAttribute("data-mapping", "pathname");
script.setAttribute("data-reactions-enabled", "1");
script.setAttribute("data-emit-metadata", "1");
script.setAttribute("data-input-position", "top");
script.setAttribute("data-lang", "en");
script.setAttribute("data-loading", "lazy");
script.setAttribute("data-theme", theme);
document.querySelector(".giscus")?.appendChild(script);
</script>Troubleshooting
Most of the ways this breaks are configuration, and the errors don’t always point at the actual cause.
- If you see “giscus is not installed on this repository”, the Giscus app isn’t installed on the repo or isn’t scoped to include it. Check the app’s repository access in your GitHub settings.
- If comments never load at all, check that the repo is public, Discussions is actually enabled in the repo settings, and your
data-repo-id/data-category-idvalues match what the configurator gave you. - If the theme flashes light before going dark, you’re setting the theme after the iframe loads. Read the stored theme and set
data-themebefore injecting the script like above. - With
data-loading="lazy"the iframe doesn’t exist until the user scrolls near it, so a postMessage sent too early does nothing. Attaching the update to thethemechangeevent handles this since it re-queries the iframe each time. - With
pathnamemapping the discussion thread is tied to the url path, so if you rename a post’s slug the old comments won’t follow it. You’d have to retitle the discussion on GitHub to match the new path.
And that’s all you have to do to get comments on your site. If you’re building out an Astro blog I’ve also written about adding a search bar, pulling live stats into a static site, and interactive charts which uses the same themechange event trick as this post.
If this helped or you got stuck somewhere, leave a comment below.