Introduction to Docusaurus
30. April, 2024 • 5 min read • Teach
Dinosaurs taking over documentation
Every project I work on eventually sprouts a docs folder, and every docs folder eventually raises the same question: where does this get published, and who has to maintain the publishing? Docusaurus is Meta's answer to that, and if your team already writes React, it is a comfortable one.
Docusaurus is a static site generator built on React, aimed squarely at project documentation rather than at blogs or marketing sites. You write Markdown, it gives you a sidebar, versioning, an internationalisation pipeline and a theme that already looks like documentation. The part I actually care about is MDX: a Markdown file can import a React component and render it inline, so a page can contain a working demo instead of a picture of one.
That sounds like a gimmick until you have maintained the docs for a component library. Screenshots go stale quietly and nobody notices for a year. A live component either renders or breaks the build.
Getting it running
You need Node.js and nothing else. Open a terminal and run:
npx create-docusaurus@latest my-docs classicThe “classic” template gives you a docs section, a blog, and the default theme. Somewhere in the prompts the installer asks whether you want JavaScript or TypeScript. Take TypeScript. It costs nothing at setup time and saves you an afternoon later, when you are squinting at docusaurus.config.ts trying to remember what shape the themeConfig object wants.
Then start the development server:
cd my-docs
npm startThat serves the site on http://localhost:3000/ with hot reload.
Things worth knowing on day one
Three details caught me out early enough that they are worth writing down.
The sidebar is either generated from your folder structure or declared by hand in sidebars.ts. Auto-generation is fine right up to the moment you care about the order of pages, and then it isn’t. Switch to an explicit sidebar early; retrofitting one across forty files is dull work.
Versioning is a CLI command rather than something clever with git:
npm run docusaurus docs:version 1.0It copies the current docs/ directory into versioned_docs/version-1.0/ and freezes it. That’s it. Run it before your documentation has settled and you will find yourself fixing the same typo in three places forever.
Search isn’t included in the build. The classic template is wired for Algolia DocSearch, which is free for open source projects but requires an application and a crawl. For anything private, @easyops-cn/docusaurus-search-local builds a static index at build time and needs no external service at all.
Running it in Docker
I don’t enjoy installing a per-project Node toolchain on my laptop, and documentation sites are exactly the kind of thing you come back to eighteen months later on a machine with a different Node version. So I containerise it.
Create a Dockerfile:
FROM node:20.13.0 AS builder
ENV NODE_PATH=/app/node_modules
WORKDIR /app
COPY package*.json /app/
RUN npm install
COPY . /app
RUN npm run build
FROM nginx:latest
COPY /app/build /usr/share/nginx/html
EXPOSE 80Two stages: the first installs dependencies and produces the static build, the second serves that build through nginx and carries no Node runtime at all. If you pull packages from a private registry, add .npmrc to the COPY line, but only if the file genuinely exists in the repository. A COPY of a missing source fails the build with an error message that takes longer to understand than it should.
For local work, a docker-compose.yml:
services:
web:
build:
context: .
target: builder
ports:
- '8000:80'
volumes:
- '.:/app:rw'
- 'node_modules:/app/node_modules'
command: npm start -- --port 80 --host 0.0.0.0
volumes:
node_modules:The target: builder line is the important one. It stops the build at the first stage, so you get the development server with hot reload instead of the nginx image. The named node_modules volume stops your host directory from shadowing the modules that were installed inside the container, which is the single most common reason a mounted Node project refuses to start.
Then:
docker compose build
docker compose upYou can turn the same repository into a template and push it to Divio Cloud, which handles certificates and environments for you. I wrote about that setup in more detail when I first moved this site over to Divio Cloud.
The alternatives
Docusaurus is not the obvious answer for every project, and picking it for a Python codebase because you happen to like React is a decision your colleagues will resent.
MkDocs is the one I reach for most often. Markdown, one YAML config file, no build toolchain to speak of. Paired with the Material theme it produces something that looks better than most hand-rolled documentation sites and takes about ten minutes to stand up.
Sphinx is the Python world’s default, and it earns that position. reStructuredText is more fiddly than Markdown, but the cross-referencing, autodoc and extension ecosystem have no real equivalent elsewhere. The django CMS documentation runs on it, as does most of the Python standard library’s.
Jekyll is worth mentioning mainly because GitHub Pages builds it natively. If your requirement is “documentation, on GitHub, with no CI pipeline”, that native support is hard to argue with.
VuePress sits in roughly the same spot as Docusaurus for teams who write Vue rather than React. Same idea, same Markdown-centred structure, different component model.
What I would pick
If the project is Python, use Sphinx or MkDocs and don’t overthink it. If the project is a React library and the documentation needs live, interactive examples, Docusaurus pays for itself quickly. If it is neither of those, MkDocs with Material is almost always enough, and “almost always enough” is an underrated property in a documentation tool.
The thing I still don’t love about Docusaurus is how much of it is React all the way down. A broken MDX import fails the production build, which is correct behaviour and mildly infuriating at 17:45 on a Friday 🙃
‘Till next time!