---
id: blog
title: Blog
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

The blog feature enables you to deploy in no time a full-featured blog.

:::info

Check the [Blog Plugin API Reference documentation](./api/plugins/plugin-content-blog.md) for an exhaustive list of options.

:::

## Initial setup {#initial-setup}

To setup your site's blog, start by creating a `blog` directory.

Then, add an item link to your blog within `docusaurus.config.js`:

```js title="docusaurus.config.js"
module.exports = {
  themeConfig: {
    // ...
    navbar: {
      items: [
        // ...
        // highlight-next-line
        {to: 'blog', label: 'Blog', position: 'left'}, // or position: 'right'
      ],
    },
  },
};
```

## Adding posts {#adding-posts}

To publish in the blog, create a Markdown file within the blog directory.

For example, create a file at `website/blog/2019-09-05-hello-docusaurus-v2.md`:

```yml title="website/blog/2019-09-05-hello-docusaurus-v2.md"
---
title: Welcome Docusaurus v2
description: This is my first post on Docusaurus 2.
slug: welcome-docusaurus-v2
authors:
  - name: Joel Marcey
    title: Co-creator of Docusaurus 1
    url: https://github.com/JoelMarcey
    image_url: https://github.com/JoelMarcey.png
  - name: Sébastien Lorber
    title: Docusaurus maintainer
    url: https://sebastienlorber.com
    image_url: https://github.com/slorber.png
tags: [hello, docusaurus-v2]
image: https://i.imgur.com/mErPwqL.png
hide_table_of_contents: false
---
Welcome to this blog. This blog is created with [**Docusaurus 2**](https://docusaurus.io/).

<!--truncate-->

This is my first post on Docusaurus 2.

A whole bunch of exploration to follow.
```

:::note

Docusaurus will extract a `YYYY-MM-DD` date from a file/folder name such as `YYYY-MM-DD-my-blog-post-title.md`.

This naming convention is optional, and you can provide the date as FrontMatter.

<details>
<summary>Example supported patterns</summary>

- `2021-05-28-my-blog-post-title.md`
- `2021-05-28-my-blog-post-title.mdx`
- `2021-05-28-my-blog-post-title/index.md`
- `2021-05-28/my-blog-post-title.md`
- `2021/05/28/my-blog-post-title.md`
- `2021/05-28-my-blog-post-title.md`
- `2021/05/28/my-blog-post-title/index.md`
- ...

</details>

:::

:::tip

Using a folder can be convenient to co-locate blog post images alongside the Markdown file.

:::

The only required field in the front matter is `title`; however, we provide options to add more metadata to your blog post, for example, author information. For all possible fields, see [the API documentation](api/plugins/plugin-content-blog.md#markdown-frontmatter).

## Blog list {#blog-list}

The blog's index page (by default, it is at `/blog`) is the _blog list page_, where all blog posts are collectively displayed.

Use the `<!--truncate-->` marker in your blog post to represent what will be shown as the summary when viewing all published blog posts. Anything above `<!--truncate-->` will be part of the summary. For example:

```yml
---
title: Truncation Example
---
All these will be part of the blog post summary.

Even this.

<!--truncate-->

But anything from here on down will not be.

Not this.

Or this.
```

By default, 10 posts are shown on each blog list page, but you can control pagination with the `postsPerPage` option in the plugin configuration. If you set `postsPerPage: 'ALL'`, pagination will be disabled and all posts will be displayed on the first page. You can also add meta description to the blog list page for better SEO:

```js title="docusaurus.config.js"
module.exports = {
  // ...
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          // highlight-start
          blogTitle: 'Docusaurus blog!',
          blogDescription: 'A Docusaurus powered blog!',
          postsPerPage: 'ALL',
          // highlight-end
        },
      },
    ],
  ],
};
```

## Blog sidebar {#blog-sidebar}

The blog sidebar displays recent blog posts. The default number of items shown is 5, but you can customize with the `blogSidebarCount` option in the plugin configuration. By setting `blogSidebarCount: 0`, the sidebar will be completely disabled, with the container removed as well. This will increase the width of the main container. Specially, if you have set `blogSidebarCount: 'ALL'`, _all_ posts will be displayed.

You can also alter the sidebar heading text with the `blogSidebarTitle` option. For example, if you have set `blogSidebarCount: 'ALL'`, instead of the default "Recent posts", you may would rather make it say "All posts":

```js title="docusaurus.config.js"
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          // highlight-start
          blogSidebarTitle: 'All posts',
          blogSidebarCount: 'ALL',
          // highlight-end
        },
      },
    ],
  ],
};
```

## Blog post authors {#blog-post-authors}

Use the `authors` FrontMatter field to declare blog post authors.

### Inline authors {#inline-authors}

Blog post authors can be declared directly inside the FrontMatter:

````mdx-code-block
<Tabs groupId="author-frontmatter">
  <TabItem value="single" label="Single author">

```yml title="my-blog-post.md"
---
authors:
  name: Joel Marcey
  title: Co-creator of Docusaurus 1
  url: https://github.com/JoelMarcey
  image_url: https://github.com/JoelMarcey.png
---
```

  </TabItem>
  <TabItem value="multiple" label="Multiple authors">

```yml title="my-blog-post.md"
---
authors:
  - name: Joel Marcey
    title: Co-creator of Docusaurus 1
    url: https://github.com/JoelMarcey
    image_url: https://github.com/JoelMarcey.png
  - name: Sébastien Lorber
    title: Docusaurus maintainer
    url: https://sebastienlorber.com
    image_url: https://github.com/slorber.png
---
```

  </TabItem>
</Tabs>
````

:::tip

This option works best to get started, or for casual, irregular authors.

:::

:::info

Prefer usage of the `authors` FrontMatter, but the legacy `author_*` FrontMatter remains supported:

```yml title="my-blog-post.md"
---
author: Joel Marcey
author_title: Co-creator of Docusaurus 1
author_url: https://github.com/JoelMarcey
author_image_url: https://github.com/JoelMarcey.png
---
```

:::

### Global authors {#global-authors}

For regular blog post authors, it can be tedious to maintain authors information inlined in each blog post.

It is possible declare those authors globally in a configuration file:

```yml title="website/blog/authors.yml"
jmarcey:
  name: Joel Marcey
  title: Co-creator of Docusaurus 1
  url: https://github.com/JoelMarcey
  image_url: https://github.com/JoelMarcey.png

slorber:
  name: Sébastien Lorber
  title: Docusaurus maintainer
  url: https://sebastienlorber.com
  image_url: https://github.com/slorber.png
```

:::tip

Use the `authorsMapPath` plugin option to configure the path. JSON is also supported.

:::

In blog posts FrontMatter, you can reference the authors declared in the global configuration file:

````mdx-code-block
<Tabs groupId="author-frontmatter">
  <TabItem value="single" label="Single author">

```yml title="my-blog-post.md"
---
authors: jmarcey
---
```

  </TabItem>
  <TabItem value="multiple" label="Multiple authors">

```yml title="my-blog-post.md"
---
authors: [jmarcey, slorber]
---
```

  </TabItem>
</Tabs>
````

:::info

The `authors` system is very flexible and can suit more advanced use-case:

<details>
  <summary>Mix inline authors and global authors</summary>

You can use global authors most of the time, and still use inline authors:

```yml title="my-blog-post.md"
---
authors:
  - jmarcey
  - slorber
  - name: Inline Author name
    title: Inline Author Title
    url: https://github.com/inlineAuthor
    image_url: https://github.com/inlineAuthor
---
```

</details>

<details>
  <summary>Local override of global authors</summary>

You can customize the global author's data on per-blog-post basis:

```yml title="my-blog-post.md"
---
authors:
  - key: jmarcey
    title: Joel Marcey's new title
  - key: slorber
    name: Sébastien Lorber's new name
---
```

</details>

<details>
  <summary>Localize the author's configuration file</summary>

The configuration file can be localized, just create a localized copy of it at:

```bash
website/i18n/[locale]/docusaurus-plugin-content-blog/authors.yml
```

</details>

:::

## Reading time {#reading-time}

Docusaurus generates a reading time estimation for each blog post based on word count. We provide an option to customize this.

```js title="docusaurus.config.js"
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          // highlight-start
          showReadingTime: true, // When set to false, the "x min read" won't be shown
          readingTime: ({content, frontMatter, defaultReadingTime}) =>
            defaultReadingTime({content, options: {wordsPerMinute: 300}}),
          // highlight-end
        },
      },
    ],
  ],
};
```

The `readingTime` callback receives three parameters: the blog content text as a string, front matter as a record of string keys and their values, and the default reading time function. It returns a number (reading time in minutes) or `undefined` (disable reading time for this page).

The default reading time is able to accept additional options: `wordsPerMinute` as a number (default: 300), and `wordBound` as a function from string to boolean. If the string passed to `wordBound` should be a word bound (spaces, tabs, and line breaks by default), the function should return `true`.

:::tip

Use the callback for all your customization needs:

````mdx-code-block
<Tabs>
<TabItem value="disable-per-post" label="Per-post disabling">

**Disable reading time on one page:**

```js title="docusaurus.config.js"
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          showReadingTime: true,
          // highlight-start
          readingTime: ({content, frontMatter, defaultReadingTime}) =>
            frontMatter.hide_reading_time ? undefined : defaultReadingTime({content}),
          // highlight-end
        },
      },
    ],
  ],
};
```

Usage:

```yml "my-blog-post.md"
---
hide_reading_time: true
---

This page will no longer display the reading time stats!
```

</TabItem>
<TabItem value="passing-options" label="Passing options">

**Pass options to the default reading time function:**

```js title="docusaurus.config.js"
module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          // highlight-start
          readingTime: ({content, defaultReadingTime}) =>
            defaultReadingTime({content, options: {wordsPerMinute: 100}}),
          // highlight-end
        },
      },
    ],
  ],
};
```

</TabItem>
<TabItem value="using-custom-algo" label="Using custom algorithms">

**Use a custom implementation of reading time:**

```js title="docusaurus.config.js"
const myReadingTime = require('./myReadingTime');

module.exports = {
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          // highlight-next-line
          readingTime: ({content}) => myReadingTime(content),
        },
      },
    ],
  ],
};
```

</TabItem>
</Tabs>
````

:::

## Feed {#feed}

You can generate RSS / Atom / JSON feed by passing `feedOptions`. By default, RSS and Atom feeds are generated. To disable feed generation, set `feedOptions.type` to `null`.

```ts
type FeedType = 'rss' | 'atom' | 'json';

type BlogOptions = {
  feedOptions?: {
    type?: FeedType | 'all' | FeedType[] | null;
    title?: string;
    description?: string;
    copyright: string;
    language?: string; // possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
  };
};
```

Example usage:

```js {8-11} title="docusaurus.config.js"
module.exports = {
  // ...
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        blog: {
          feedOptions: {
            type: 'all',
            copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
          },
        },
      },
    ],
  ],
};
```

The feeds can be found at:

<Tabs>
<TabItem value="RSS">

```text
https://example.com/blog/rss.xml
```

</TabItem>
<TabItem value="Atom">

```text
https://example.com/blog/atom.xml
```

</TabItem>
<TabItem value="JSON">

```text
https://example.com/blog/feed.json
```

</TabItem>
</Tabs>

## Advanced topics {#advanced-topics}

### Blog-only mode {#blog-only-mode}

You can run your Docusaurus 2 site without a dedicated landing page and instead have your blog's post list page as the index page. Set the `routeBasePath` to be `'/'` to serve the blog through the root route `example.com/` instead of the subroute `example.com/blog/`.

```js title="docusaurus.config.js"
module.exports = {
  // ...
  presets: [
    [
      '@docusaurus/preset-classic',
      {
        // highlight-next-line
        docs: false, // Optional: disable the docs plugin
        blog: {
          // highlight-next-line
          routeBasePath: '/', // Serve the blog at the site's root
          /* other blog options */
        },
      },
    ],
  ],
};
```

:::caution

Don't forget to delete the existing homepage at `./src/pages/index.js` or else there will be two files mapping to the same route!

:::

:::tip

There's also a "Docs-only mode" for those who only want to use the docs. Read [Docs-only mode](./guides/docs/docs-introduction.md) for detailed instructions or a more elaborated explanation of `routeBasePath`.

:::

### Multiple blogs {#multiple-blogs}

By default, the classic theme assumes only one blog per website and hence includes only one instance of the blog plugin. If you would like to have multiple blogs on a single website, it's possible too! You can add another blog by specifying another blog plugin in the `plugins` option for `docusaurus.config.js`.

Set the `routeBasePath` to the URL route that you want your second blog to be accessed on. Note that the `routeBasePath` here has to be different from the first blog or else there could be a collision of paths! Also, set `path` to the path to the directory containing your second blog's entries.

As documented for [multi-instance plugins](./using-plugins.md#multi-instance-plugins-and-plugin-ids), you need to assign a unique id to the plugins.

```js title="docusaurus.config.js"
module.exports = {
  // ...
  plugins: [
    [
      '@docusaurus/plugin-content-blog',
      {
        /**
         * Required for any multi-instance plugin
         */
        id: 'second-blog',
        /**
         * URL route for the blog section of your site.
         * *DO NOT* include a trailing slash.
         */
        routeBasePath: 'my-second-blog',
        /**
         * Path to data on filesystem relative to site dir.
         */
        path: './my-second-blog',
      },
    ],
  ],
};
```

As an example, we host a second blog [here](/tests/blog).
