Skip to main content

Customizing Docusaurus components

· 3 min read
Stephan Hochdörfer

Tech changes, quite rapidly. Coming across a blog post that covers outdated information happens frequently to me. Years ago, I came across a blog that displayed a "This blog post might be outdated banner" and I liked that approach.

For years, I wanted to add such a banner to our old blog system but I never managed to accomplish that task. When we rebooted our blog last year, I decided that it is time to follow the idea now. And finally, I managed to implement it. Now, if you open a blog post that was published more than a year ago, you'll see a banner like this:

This blogpost is outdated banner

And this is how I implemented this feature in our Docusaurus blog:

First, you have to eject the component you want to customize. Ejecting a Docusaurus theme component basically creates a copy of the original theme component in your local code. This allows you to fully customize and override that specific component.

In our case, we want to add a custom component to the BlogPostItem/Header component, so let's eject this component:

yarn swizzle @docusaurus/theme-classic "BlogPostItem/Header" --eject

This results in the following files getting created locally:

src/theme/BlogPostItem/Header/index.js
src/theme/BlogPostItem/Header/Author/index.js
src/theme/BlogPostItem/Header/Authors/index.js
src/theme/BlogPostItem/Header/Authors/styles.module.css
src/theme/BlogPostItem/Header/Info/index.js
src/theme/BlogPostItem/Header/Info/styles.module.css
src/theme/BlogPostItem/Header/Title/index.js
src/theme/BlogPostItem/Header/Title/styles.module.css

We are only interested in the file src/theme/BlogPostItem/Header/index.js, the other files can be deleted because we won't be touching those components.

We add a BlogPostItemBannerOutdated component to the index.js file by adding the import statement as well the corresponding <BlogPostItemBannerOutdated /> element in the HTML part:

import React from 'react';
import BlogPostItemHeaderTitle from '@theme/BlogPostItem/Header/Title';
import BlogPostItemHeaderInfo from '@theme/BlogPostItem/Header/Info';
import BlogPostItemHeaderAuthors from '@theme/BlogPostItem/Header/Authors';
import BlogPostItemBannerOutdated from './BannerOutdated/index';

export default function BlogPostItemHeader() {
return (
<header>
<BlogPostItemHeaderTitle />
<BlogPostItemBannerOutdated />
<BlogPostItemHeaderInfo />
<BlogPostItemHeaderAuthors />
</header>
);
}

The BlogPostItemBannerOutdated component itself looks like this:

import React from 'react';
import clsx from 'clsx';
import {useBlogPost} from '@docusaurus/theme-common/internal';
import type {Props} from '@theme/BlogPostItem/Header/Authors';
import styles from './styles.module.css';

export default function BlogPostItemBannerOutdated({
className,
}: Props): JSX.Element | null {
const oneDayInMilliseconds = 1000 * 60 * 60 * 24;
const oneYearInMilliseconds = 365 * oneDayInMilliseconds;
const blogpost = useBlogPost();

if (blogpost.frontMatter.hide_outdated_banner === undefined) {
blogpost.frontMatter.hide_outdated_banner = false;
}

if (blogpost.frontMatter.hide_outdated_banner) {
return null;
}

const publishedDate = Date.parse(blogpost.metadata.date);
const today = Date.now();

if ((today - publishedDate) < oneYearInMilliseconds) {
return null;
}

return (
<div
className={clsx(
'margin-top--md margin-bottom--sm',
className,
)}>
<strong>This blog post was published more than one year ago and might be outdated!</strong>
</div>
);
}

The logic works like this: We check if the blogpost was published more than 365 days ago by comparing the date of the blog post with the calculated day that represents today 1 year in the past.

While this logic applies to most but not all blogposts, it was needed to add a flag to the blog post itself to activate or deactivate this feature. By default, the feature is active. If you set hide_outdated_banner property to true for a specific blogpost, then the logic is not executed. This way, some blogposts, e.g. interviews with our employees are not marked as outdated.

The hide_outdated_banner option from blogpost source files written in Markdown is exposed via the blogpost.frontMatter.hide_outdated_banner property. Since some blogposts might not have the property set, a little fallback logic was added, to avoid any Javascript errors:

if (blogpost.frontMatter.hide_outdated_banner === undefined) {
blogpost.frontMatter.hide_outdated_banner = false;
}

All in all, I was impressed by how easy it is to customize Docusaurus. And I really like this new feature of our blog. What do you think?