DEV Community

Cover image for Docusaurus Rocks!
Amnish Singh Arora
Amnish Singh Arora

Posted on • Updated on

Docusaurus Rocks!

This week, I looked into Docusaurus, one of the best tools to generate a beautiful documentation site in no time. Known for its simplicity and ease of use, it provides a clean and modern design, search functionality, and focuses on creating an easy to maintain documentation for developers. No wonder it has a thriving community.

In this post, I'll be sharing my code reading results for one of the features I would like to implement in my own site-generation project.

Table of Contents

 1. Feature - TOC
 2. Reading the docs 📘
 3. Looking at the code 💻
 4. Finding implementation for TOC
 5. Conclusion 🎇

Feature - TOC

The feature I'll be talking about is a common but a really handy utility. Apart from grouping various related documents into a comprehensive navbar on the left, every page on a Docusaurus generated site can be configured with Table of Contents. Since it allows the readers to skim through large pieces of writings, it is a must-have on any platform that publishes readings.

Sample TOC

Reading the docs 📘

I started my research by looking through the official docs that go through different types of TOCs and how a TOC can be configured.

The docs talked about 2 types of TOCs - the one that is always there on the right side, and an inline version you can have at the top.

Inline TOC

I also noticed some interesting features, that allow users to configure their TOCs - in either the document itself or in the docusaurus config file.

TOC Configurations

Looking at the code 💻

After going through any docs concerning TOCs, I decided to dive into the actual code to get a sense of how I could add a similar feature to my project.

Since its way easier to inspect code in an IDE compared to Github, I forked the repository and cloned my copy on the system.

Docusaurus file structure

I took a quick glance at the file structure and was stunned by the project's sheer size and level of complexity. All the major building blocks were isolated in their own separate packages, essentially making the project a collection of smaller projects.

Finding implementation for TOC

After randomly opening folders to get a basic idea of how everything was organized (with little success), I realized the only option was to start reading with a goal.

1. Finding a lead

The very first thing I had to figure out was where to start looking in the massive file structure I just witnessed. In other words, I needed a keyword that would lead me to the correct files.

This part is sometimes really easy when you have a UI to inspect at your disposal. I opened the browser tools and started looking at the TOC html.

Inspecting TOC

As expected, I found a class name "tableOfContents" that could easily lead me to the files and code I was looking for.

2. Using the keyword to search

Putting faith in the class name I had found, I immediately put it in my VSCode search bar to see how close I could get with it.

Searching for class name

And lucky me, I found exactly what I was looking for. I found all the implementation files, and even tests related to the feature in the first attempt.

Link to tests: https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-theme-classic/src/__tests__/options.test.ts

The config files are in the generated project itself.

3. Making sense of the code found

Now it was time to dive into those files, and get an idea of how things were done.

The first thing I found was the well-designed schema for configuring the project's theme, and TOC was a part of it.

docusaurus/packages/docusaurus-theme-classic/src/options.ts

export const DEFAULT_CONFIG: ThemeConfig = {
  colorMode: DEFAULT_COLOR_MODE_CONFIG,
  docs: DEFAULT_DOCS_CONFIG,
  metadata: [],
  prism: {
    additionalLanguages: [],
    theme: defaultPrismTheme,
    magicComments: [
      {
        className: 'theme-code-block-highlighted-line',
        line: 'highlight-next-line',
        block: {start: 'highlight-start', end: 'highlight-end'},
      },
    ],
  },
  navbar: {
    hideOnScroll: false,
    items: [],
  },
  tableOfContents: {
    minHeadingLevel: 2,
    maxHeadingLevel: 3,
  },
};
Enter fullscreen mode Exit fullscreen mode

Not only this, I found various react components for different types of TOCs (sidebar and inline).

TOC folders

Since I was planning to implement an inline TOC (although logic for all types would be similar only styling differs), I opened the TOCInline component.

docusaurus\packages\docusaurus-theme-classic\src\theme\TOCInline\index.tsx
 raw `TOCInline` endraw  component

Now that I found the meat, it was time to follow the arrows.

Follow the arrows

Apparenty, all different types of TOCs (sidebar, collapsible, inline) were just a wrapper to a more generic component containing the core TOC logic and html TOCItems.

docusaurus\packages\docusaurus-theme-classic\src\theme\TOCItems\index.tsx

export default function TOCItems({
  toc,
  className = 'table-of-contents table-of-contents__left-border',
  linkClassName = 'table-of-contents__link',
  linkActiveClassName = undefined,
  minHeadingLevel: minHeadingLevelOption,
  maxHeadingLevel: maxHeadingLevelOption,
  ...props
}: Props): JSX.Element | null {
  const themeConfig = useThemeConfig();

  const minHeadingLevel =
    minHeadingLevelOption ?? themeConfig.tableOfContents.minHeadingLevel;
  const maxHeadingLevel =
    maxHeadingLevelOption ?? themeConfig.tableOfContents.maxHeadingLevel;

  const tocTree = useFilteredAndTreeifiedTOC({
    toc,
    minHeadingLevel,
    maxHeadingLevel,
  });

  const tocHighlightConfig: TOCHighlightConfig | undefined = useMemo(() => {
    if (linkClassName && linkActiveClassName) {
      return {
        linkClassName,
        linkActiveClassName,
        minHeadingLevel,
        maxHeadingLevel,
      };
    }
    return undefined;
  }, [linkClassName, linkActiveClassName, minHeadingLevel, maxHeadingLevel]);
  useTOCHighlight(tocHighlightConfig);

  return (
    <TOCItemTree
      toc={tocTree}
      className={className}
      linkClassName={linkClassName}
      {...props}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

And finally, I was led to the TOCItemsTree component which was the first piece of code that made my jaws drop.

docusaurus\packages\docusaurus-theme-classic\src\theme\TOCItems\Tree.tsx
TOCItemsTree

A recursive component!

At first, I couldn't belive what I saw. I always knew these treeviews and TOCs should have a tree data structure under the hood, but never thought the UI was also done in a tree style recusrive fashion. This was the most interesting thing I had found so far in Docusaurus, and I am glad to pick this feature.

Another interseting thing I found was the consistent use of clsx library throughout the codebase.

Clsx in code

I had never heard of this library before, but on a quick chat with the one and only - ChatGPT, I found that it was one of the libraries I was always looking for.

Clsx Explanation by chatgpt

I learnt React in my college's web422 course, which was not long ago, and always wondered if there was a better way to add customized classnames in the jsx.

I have a lot of experience with Angular from my coop, and that framework has multiple powerfull strategies to configure styles/classes - NgStyle, NgClass, and interpolation to name a few.

Angular is better than React, change my mind in comments!

PS: Just my opinion, both are great technologies. And everyone might have different favourites.
Here's an article for a neutral comparison.
Angular vs React: Which One to Choose for Your App

But anyways, finding something powerfull for this purpose in React was really nice - more weapons in my arsenal now.

Conclusion 🎇

And that is a summary of my findings when looking into docusaurus' code for ideas and inspiration. I learnt about various unexpected concepts/tools like recursive components, clsx library and some ideas to organize projects.

I'll be following up with a post soon, about how I add this feature to my own project.

In the meantime, stay tuned!

Top comments (10)

Collapse
 
hikionori profile image
Danila Verbinsky

So I don't have any don't about Angular, but React lighter than Angular and his more suitable for pet project, for commercial use cases I prefer use NextJS, so situation the same with AstroJS(anyway AstroJS have own solution for docs). Conclusion of my comment: Clear React only for pet project (same as Astro), Angular and NextJS for commercial use. Try Starlight I think that's what it's called.

Collapse
 
amnish04 profile image
Amnish Singh Arora • Edited

Agreed, Next.js is also really good. One thing I would say React has over Angular is a larger community, a greater variety of libraries and flexibility in solutions and designs.

Still, Angular will always win for me, just because of my personal opinion. Although, both are great frameworks and everyone may have different favorites.

Collapse
 
jay_js profile image
Jayesh Kale

well i might suggest more simple gohugo if you're interested

Collapse
 
amnish04 profile image
Amnish Singh Arora • Edited

Hey, thanks for suggesting. I never knew about that. Will definitely give it a try!

Collapse
 
christopherxp profile image
Christopherxp

Great post

Collapse
 
amnish04 profile image
Amnish Singh Arora

Thank you!

Collapse
 
hesxenon profile image
hesxenon

You may find this comment snarky, but if recursive components are a new concept to you you probably aren't qualified to make statements like "x is better than y".

Components are simply the subset of functions data -> ui and thus can be recursive - even if some frameworks go to great lengths to make it look like they're "classes".

Write something like docusaurus with react and with angular and then write an article about what you liked better in one or the other.

I did like that you took the time to look at the actual code and were able to find your way around a foreign codebase :)

Collapse
 
amnish04 profile image
Amnish Singh Arora • Edited

You should understand that it was a playful remark, and simply my opinion. As developers, we learn new things everyday. But if I saw something today, that you might have known a long time ago, that doesn’t make me ‘unqualified’ to express my views.

I liked your idea of writing a comparison post. I made that remark after reading more than a bunch of such articles, and building/contributing to projects using both.

I agree that both are one of the best frontend technologies out there, and one may be a better choice over the other depending on the scale, complexity and nature of the project.

I could have structured my remark in a better way, will make it clearer.
But thanks for your comment :)

Collapse
 
hesxenon profile image
hesxenon

It's not about me having known something for a long time, it's about you stating your opinion as fact when you don't have the basis for that argument :)

I'm really not trying to offend or belittle you here, but not every statement is created equal or should be made in the first place. People who might just be starting out are reading your posts and might think "hey, this mentor-like person" says angular is best, let's invest into that - and a few years down the road they find out that angular is currently one of the most hated FE frameworks out there with a really problematic ecosystem and even more problematic core values and concepts.

And honestly regarding the actual angular vs. react debate... that debate can't be had without factoring in a multitude of things that would probably require its own series of blog posts.

Contributing to projects in one or the other is great, but doesn't really highlight the problems of either imho - this you can only find out if you create something start to finish.

Thread Thread
 
amnish04 profile image
Amnish Singh Arora • Edited

The basis for me having that thought in mind and adding a remark was that you need an external library in react to even apply css class names conditionally.
I agree with you on that an extreme statement without context may confuse or even mislead newcomers.
To acknowledge that, I have added links to a couple of articles that provide a detailed discussion for the topic, and added a note :D