Hello Docusaurus!
I've investigated different static site generators — SSGs — in order to set up a proper blog for this website. I've read about a lot of them, tried a few. My favourite by far is Docusaurus, the one this site is now built with.
Quick comparison
As I'm working on complex web apps during the day, SSGs make evening projects feel like holiday.
Still, I found that many of them require quite some tinkering before achieving desired results. In theory, to have a quick website set up, we can start from a template. But unfortunately this is where most of the pain come from. Many templates I looked at where incomplete, old, unmaintained, or simply ugly.
Docusaurus is the one which has the best of both:
- A great template to start with.
There is only one at the moment. It has basically everything I wanted and it's very nice. So it's much better than a choice of 100 useless templates. It might be a bit of a problem for someone really not liking it... but still, wait for the next point.
- A great way to tinker and customise it.
Docusaurus provides multiple ways to customise the styling and layout, depending on what we want to change, and how deep we want to change it. From creating React components and use them in MDX, to the brilliant swizzling method allowing to easily cherry pick a component to wrap it or replace it (there is an example in the Add comment system section below).
Setup
I will write about the choices I've made during the building of this blog. I won't detail every steps — official docs are better sources and will stay up to date.
Create a new Docusaurus project
npx create-docusaurus@latest your-project-name classic --typescript
This is all what's needed to have a complete website with dummy pages ready to be filled with content.
If you're sick of having a new browser tab being opened every time you run the start
command, append --no-open
.
Setup Prettier
Prettier formats Markdown well, it will be appreciated while writing blog posts.
Set up redirect to /blog
I wanted the blog to be the only content of the site for the moment. But I didn't want to make the blog base URL simply /
, as all the post links would be in the format <domain>/<post>
, which could potentially become annoying if I wanted to add other types of content to the site in the future.
So I kept the blog base URL /blog
(post links are <domain>/blog/<post>
), and I created a redirect from /
to /blog
.
Server-side redirects are best, but as I'm hosting the site on GitHub Pages, this isn't an option.
Docusaurus provides a plugin for client-side redirects:
npm i @docusaurus/plugin-client-redirects
plugins: [
[
"@docusaurus/plugin-client-redirects",
/** @type {import('@docusaurus/plugin-client-redirects').Options} */
({
redirects: [
{
from: "/",
to: "/blog",
},
],
}),
],
],
Deactivate docs
presets: [
[
"classic",
...
docs: false,
Add metadata
Adding the following makes links to the blog look pretty when shared.
presets: [
[
"classic",
...
blog: {
blogTitle: "<Blog title>",
blogDescription: "<Blog description>",
themeConfig:
...
image: "<Image path>",
Activate all feed types
Just because I want all the shiny new stuff, I activated all the types of feeds.
presets: [
[
"classic",
...
blog: {
feedOptions: {
type: "all",
title: "<Blog title>", // By default, it's in the format `<Site name> Blog`
description: "<Blog description>", // Same here
},
Add comment system
Giscus is a great comment system powered by GitHub Discussions.
Install its React component:
npm i @giscus/react
Use giscus.app to set up and retrieve your configuration for Giscus, then create the file src/components/GiscusComments.tsx
:
import { useColorMode } from "@docusaurus/theme-common";
import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
import Giscus from "@giscus/react";
import React from "react";
export default function GiscusComments() {
const {
siteConfig: {
customFields: {
giscusRepo,
giscusRepoId,
giscusCategory,
giscusCategoryId,
},
},
} = useDocusaurusContext();
const { colorMode } = useColorMode();
if (
typeof giscusRepo !== "string" ||
typeof giscusRepoId !== "string" ||
typeof giscusCategory !== "string" ||
typeof giscusCategoryId !== "string"
) {
return null;
}
return (
<Giscus
repo={giscusRepo as `${string}/${string}`}
repoId={giscusRepoId}
category={giscusCategory}
categoryId={giscusCategoryId}
mapping="pathname"
strict="0"
reactionsEnabled="1"
emitMetadata="0"
inputPosition="bottom"
theme={colorMode}
lang="en"
/>
);
}
I used Docusaurus' custom fields to retrieve Giscus configuration from Docusaurus' config (itself retrieving these values using dotenv
)
In order to add the Giscus component to our blog posts, we are going to use Docusaurus' swizzling method to wrap one component of the template (read about swizzling here):
npm run swizzle @docusaurus/theme-classic BlogPostItem --wrap
This command creates a new file:
src/theme/BlogPostItem/index.js
which we can directly rename to .tsx
:
src/theme/BlogPostItem/index.tsx
Now we need to restart Docusaurus' dev server, in order for it to take the new custom component into account.
This file is a wrapper for the BlogPostItem
component. We can very easily add our new GiscusComments
component just below it:
import useIsBrowser from "@docusaurus/useIsBrowser";
import GiscusComments from "@site/src/components/GiscusComments";
import BlogPostItem from "@theme-original/BlogPostItem";
import React from "react";
const POST_REGEX = /^\/blog\/.+$/;
export default function BlogPostItemWrapper(props) {
const isBrowser = useIsBrowser();
return (
<>
<BlogPostItem {...props} />
{isBrowser && window.location.pathname.match(POST_REGEX) && (
<>
<div style={{ marginTop: "32px" }} />
<GiscusComments />
<div style={{ marginTop: "-24px" }} />
</>
)}
</>
);
}
- We check that the
pathname
matches/blog/...
to not show the Giscus component below all the posts on the main listing page (/blog
). isBrowser
is to prevent evaluatingwindow
at build time, which would creates an error.
Last thing, to be done once your local testing is finish, is to add a file giscus.json
at the root of the repository to prevent other websites from showing your discussions:
{
"origins": ["https://<your-domain>"]
}
Giscus will stop showing on your localhost:3000
after this file is committed.
Automatically deploy to GitHub Pages, Netlify, etc.
The site is deployed on very push to the main
branch. The code should be self-explanatory with enough knowledge on GitHub Actions and GitHub Pages.