Hosting a thin static blog on a platform like GitHub Pages has numerous advantages, but also takes away some interactivity. Fortunately, Giscus exists and offers a way to embed user comments on static sites.
Table of contents
Open Table of contents
How Giscus works
Giscus uses the GitHub API to read and store comments made by GitHub users in the Discussions
associated with a repository.
Embed the Giscus client-side script bundle on your site, configure it with the correct repository URL, and users can view and write comments (when logged into GitHub).
The approach is serverless, as the comments are stored on GitHub and dynamically loaded from there on client side, hence perfect for a static blog, like AstroPaper.
Setting up Giscus
Giscus can be set up easily on giscus.app, but I will outline the process shortly still.
Prequisites
Prequisites to get Giscus working are
- the repository is public
- the Giscus app is installed
- the Discussions feature is turned on for your repository
If any of these conditions cannot be fulfilled for any reason, unfortunately, Giscus cannot be integrated.
Configuring Giscus
Next, configuring Giscus is necessary. In most cases, the preselected defaults are suitable, and you should only modify them if you have a specific reason and know what you are doing. Don’t worry too much about making the wrong choices; you can always adjust the configuration later on.
However you need to
- select the right language for the UI
- specify the GitHub repository you want to connect, typically the repository containing your statically hosted AstroPaper blog on GitHub Pages
- create and set an
Announcement
type discussion on GitHub if you want to ensure nobody can create random comments directly on GitHub - define the color scheme
After configuring the settings, Giscus provides you with a generated <script>
tag, which you will need in the next steps.
Simple script tag
You should now have a script tag that looks like this:
<script
src="https://giscus.app/client.js"
data-repo="[ENTER REPO HERE]"
data-repo-id="[ENTER REPO ID HERE]"
data-category="[ENTER CATEGORY NAME HERE]"
data-category-id="[ENTER CATEGORY ID HERE]"
data-mapping="pathname"
data-strict="0"
data-reactions-enabled="1"
data-emit-metadata="0"
data-input-position="bottom"
data-theme="preferred_color_scheme"
data-lang="en"
crossorigin="anonymous"
async
></script>
Simply add that to the source code of the site. Most likely, if you’re using AstroPaper and want to enable comments on posts, navigate to src/layouts/PostDetails.astro
and paste it into the desired location where you want the comments to appear, perhaps underneath the Share this post on:
buttons.
<ShareLinks />
</div>
+ <script src="https://giscus.app/client.js"
+ data-repo="[ENTER REPO HERE]"
+ data-repo-id="[ENTER REPO ID HERE]"
+ data-category="[ENTER CATEGORY NAME HERE]"
+ data-category-id="[ENTER CATEGORY ID HERE]"
+ ...
+ </script>
</main>
<Footer />
</Layout>
And it’s done! You have successfully integrated comments in AstroPaper!
React component with light/dark theme
The embedded script tag in the layout is quite static, with the Giscus configuration, including theme
, hardcoded into the layout. Given that AstroPaper features a light/dark theme toggle, it would be nice for the comments to seamlessly transition between light and dark themes along with the rest of the site. To achieve this, a more sophisticated approach to embedding Giscus is required.
Firstly, we are going to install the React component for Giscus:
npm i @giscus/react
Then we create a new Comments.tsx
React component in src/components
:
import Giscus, { type Theme } from "@giscus/react";
import { GISCUS } from "@config";
import { useEffect, useState } from "react";
interface CommentsProps {
lightTheme?: Theme;
darkTheme?: Theme;
}
export default function Comments({
lightTheme = "light",
darkTheme = "dark",
}: CommentsProps) {
const [theme, setTheme] = useState(() => {
const currentTheme = localStorage.getItem("theme");
const browserTheme = window.matchMedia("(prefers-color-scheme: dark)")
.matches
? "dark"
: "light";
return currentTheme || browserTheme;
});
useEffect(() => {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = ({ matches }: MediaQueryListEvent) => {
setTheme(matches ? "dark" : "light");
};
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}, []);
useEffect(() => {
const themeButton = document.querySelector("#theme-btn");
const handleClick = () => {
setTheme(prevTheme => (prevTheme === "dark" ? "light" : "dark"));
};
themeButton?.addEventListener("click", handleClick);
return () => themeButton?.removeEventListener("click", handleClick);
}, []);
return (
<div className="mt-8">
<Giscus theme={theme === "light" ? lightTheme : darkTheme} {...GISCUS} />
</div>
);
}
This React component not only wraps the native Giscus component, but also introduces additional props, namely lightTheme
and darkTheme
. Leveraging two event listeners, the Giscus comments will align with the site’s theme, dynamically switching between dark and light themes whenever the site or browser theme is changed.
We also need to define the GISCUS
config, for which the optimal location is in src/config.ts
:
import type { GiscusProps } from "@giscus/react";
...
export const GISCUS: GiscusProps = {
repo: "[ENTER REPO HERE]",
repoId: "[ENTER REPO ID HERE]",
category: "[ENTER CATEGORY NAME HERE]",
categoryId: "[ENTER CATEGORY ID HERE]",
mapping: "pathname",
reactionsEnabled: "0",
emitMetadata: "0",
inputPosition: "bottom",
lang: "en",
loading: "lazy",
};
Note that specifying a theme
here will override the lightTheme
and darkTheme
props, resulting in a static theme setting, similar to the previous approach of embedding Giscus with the <script>
tag.
To complete the process, add the new Comments component to src/layouts/PostDetails.astro
(replacing the script
tag from the previous step).
+ import Comments from "@components/Comments";
<ShareLinks />
</div>
+ <Comments client:only="react" />
</main>
<Footer />
</Layout>
And that’s it!