chore: release Docusaurus v3.4 (#10186)

This commit is contained in:
Sébastien Lorber 2024-05-31 19:09:06 +02:00 committed by GitHub
parent a7afd9cc87
commit dbdd4dfb2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
145 changed files with 21407 additions and 203 deletions

View file

@ -0,0 +1,140 @@
---
slug: /creating-pages
sidebar_label: Pages
---
# Creating Pages
In this section, we will learn about creating pages in Docusaurus.
The `@docusaurus/plugin-content-pages` plugin empowers you to create **one-off standalone pages** like a showcase page, playground page, or support page. You can use React components, or Markdown.
:::note
Pages do not have sidebars, only [docs](./docs/docs-introduction.mdx) do.
:::
:::info
Check the [Pages Plugin API Reference documentation](./../api/plugins/plugin-content-pages.mdx) for an exhaustive list of options.
:::
## Add a React page {#add-a-react-page}
React is used as the UI library to create pages. Every page component should export a React component, and you can leverage the expressiveness of React to build rich and interactive content.
Create a file `/src/pages/helloReact.js`:
```jsx title="/src/pages/helloReact.js"
import React from 'react';
import Layout from '@theme/Layout';
export default function Hello() {
return (
<Layout title="Hello" description="Hello React Page">
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '50vh',
fontSize: '20px',
}}>
<p>
Edit <code>pages/helloReact.js</code> and save to reload.
</p>
</div>
</Layout>
);
}
```
Once you save the file, the development server will automatically reload the changes. Now open [`http://localhost:3000/helloReact`](http://localhost:3000/helloReact) and you will see the new page you just created.
Each page doesn't come with any styling. You will need to import the `Layout` component from `@theme/Layout` and wrap your contents within that component if you want the navbar and/or footer to appear.
:::tip
You can also create TypeScript pages with the `.tsx` extension (`helloReact.tsx`).
:::
## Add a Markdown page {#add-a-markdown-page}
Create a file `/src/pages/helloMarkdown.md`:
```md title="/src/pages/helloMarkdown.md"
---
title: my hello page title
description: my hello page description
hide_table_of_contents: true
---
# Hello
How are you?
```
In the same way, a page will be created at [`http://localhost:3000/helloMarkdown`](http://localhost:3000/helloMarkdown).
Markdown pages are less flexible than React pages because it always uses the theme layout.
Here's an [example Markdown page](/examples/markdownPageExample).
:::tip
You can use the full power of React in Markdown pages too, refer to the [MDX](https://mdxjs.com/) documentation.
:::
## Routing {#routing}
If you are familiar with other static site generators like Jekyll and Next, this routing approach will feel familiar to you. Any JavaScript file you create under `/src/pages/` directory will be automatically converted to a website page, following the `/src/pages/` directory hierarchy. For example:
- `/src/pages/index.js` → `[baseUrl]`
- `/src/pages/foo.js` → `[baseUrl]/foo`
- `/src/pages/foo/test.js` → `[baseUrl]/foo/test`
- `/src/pages/foo/index.js` → `[baseUrl]/foo/`
In this component-based development era, it is encouraged to co-locate your styling, markup, and behavior together into components. Each page is a component, and if you need to customize your page design with your own styles, we recommend co-locating your styles with the page component in its own directory. For example, to create a "Support" page, you could do one of the following:
- Add a `/src/pages/support.js` file
- Create a `/src/pages/support/` directory and a `/src/pages/support/index.js` file.
The latter is preferred as it has the benefits of letting you put files related to the page within that directory. For example, a CSS module file (`styles.module.css`) with styles meant to only be used on the "Support" page.
:::note
This is merely a recommended directory structure, and you will still need to manually import the CSS module file within your component module (`support/index.js`).
:::
By default, any Markdown or JavaScript file starting with `_` will be ignored and no routes will be created for that file (see the `exclude` option).
```bash
my-website
├── src
│ └── pages
│ ├── styles.module.css
│ ├── index.js
│ ├── _ignored.js
│ ├── _ignored-folder
│ │ ├── Component1.js
│ │ └── Component2.js
│ └── support
│ ├── index.js
│ └── styles.module.css
.
```
:::warning
All JavaScript/TypeScript files within the `src/pages/` directory will have corresponding website paths generated for them. If you want to create reusable components into that directory, use the `exclude` option (by default, files prefixed with `_`, test files(`.test.js`), and files in `__tests__` directory are not turned into pages).
:::
### Duplicate Routes {#duplicate-routes}
You may accidentally create multiple pages that are meant to be accessed on the same route. When this happens, Docusaurus will warn you about duplicate routes when you run `yarn start` or `yarn build` (behavior configurable through the [`onDuplicateRoutes`](../api/docusaurus.config.js.mdx#onDuplicateRoutes) config), but the site will still be built successfully. The page that was created last will be accessible, but it will override other conflicting pages. To resolve this issue, you should modify or remove any conflicting routes.

View file

@ -0,0 +1,192 @@
---
id: create-doc
description: Create a Markdown Document
slug: /create-doc
---
# Create a doc
Create a Markdown file, `greeting.md`, and place it under the `docs` directory.
```bash
website # root directory of your site
├── docs
│ └── greeting.md
├── src
│ └── pages
├── docusaurus.config.js
├── ...
```
```md
---
description: Create a doc page with rich content.
---
# Hello from Docusaurus
Are you ready to create the documentation site for your open source project?
## Headers
will show up on the table of contents on the upper right
So that your users will know what this page is all about without scrolling down or even without reading too much.
## Only h2 and h3 will be in the TOC by default.
You can configure the TOC heading levels either per-document or in the theme configuration.
The headers are well-spaced so that the hierarchy is clear.
- lists will help you
- present the key points
- that you want your users to remember
- and you may nest them
- multiple times
```
:::note
All files prefixed with an underscore (`_`) under the `docs` directory are treated as "partial" pages and will be ignored by default.
Read more about [importing partial pages](../markdown-features/markdown-features-react.mdx#importing-markdown).
:::
## Doc front matter {#doc-front-matter}
The [front matter](../markdown-features/markdown-features-intro.mdx#front-matter) is used to provide additional metadata for your doc page. Front matter is optional—Docusaurus will be able to infer all necessary metadata without the front matter. For example, the [doc tags](#doc-tags) feature introduced below requires using front matter. For all possible fields, see [the API documentation](../../api/plugins/plugin-content-docs.mdx#markdown-front-matter).
## Doc tags {#doc-tags}
Tags are declared in the front matter and introduce another dimension of categorization in addition to the [docs sidebar](./sidebar/index.mdx).
It is possible to define tags inline, or to reference predefined tags declared in a [`tags file`](../../api/plugins/plugin-content-docs.mdx#tags-file) (optional, usually `docs/tags.yml`).
In the following example:
- `docusaurus` references a predefined tag key declared in `docs/tags.yml`
- `Releases` is an inline tag, because it does not exist in `docs/tags.yml`
```md title="docs/my-doc.md"
---
tags:
- Releases
- docusaurus
---
# Title
Content
```
```yml title="docs/tags.yml"
docusaurus:
label: 'Docusaurus'
permalink: '/docusaurus'
description: 'Docs related to the Docusaurus framework'
```
:::tip
Tags can also be declared with `tags: [Demo, Getting started]`.
Read more about all the possible [Yaml array syntaxes](https://www.w3schools.io/file/yaml-arrays/).
:::
## Organizing folder structure {#organizing-folder-structure}
How the Markdown files are arranged under the `docs` folder can have multiple impacts on Docusaurus content generation. However, most of them can be decoupled from the file structure.
### Document ID {#document-id}
Every document has a unique `id`. By default, a document `id` is the name of the document (without the extension) relative to the root docs directory.
For example, the ID of `greeting.md` is `greeting`, and the ID of `guide/hello.md` is `guide/hello`.
```bash
website # Root directory of your site
└── docs
├── greeting.md
└── guide
└── hello.md
```
However, the **last part** of the `id` can be defined by the user in the front matter. For example, if `guide/hello.md`'s content is defined as below, its final `id` is `guide/part1`.
```md
---
id: part1
---
Lorem ipsum
```
The ID is used to refer to a document when hand-writing sidebars, or when using docs-related layout components or hooks.
### Doc URLs {#doc-urls}
By default, a document's URL location is its file path relative to the `docs` folder, with a few exceptions. Namely, if a file is named one the following, the file name won't be included in the URL:
- Named as `index` (case-insensitive): `docs/Guides/index.md`
- Named as `README` (case-insensitive): `docs/Guides/README.mdx`
- Same name as parent folder: `docs/Guides/Guides.md`
In all cases, the default slug would only be `/Guides`, without the `/index`, `/README`, or duplicate `/Guides` segment.
:::note
This convention is exactly the same as [the category index convention](./sidebar/autogenerated.mdx#category-index-convention). However, the `isCategoryIndex` configuration does _not_ affect the document URL.
:::
Use the `slug` front matter to change a document's URL.
For example, suppose your site structure looks like this:
```bash
website # Root directory of your site
└── docs
└── guide
└── hello.md
```
By default `hello.md` will be available at `/docs/guide/hello`. You can change its URL location to `/docs/bonjour`:
```md
---
slug: /bonjour
---
Lorem ipsum
```
`slug` will be appended to the doc plugin's `routeBasePath`, which is `/docs` by default. See [Docs-only mode](docs-introduction.mdx#docs-only-mode) for how to remove the `/docs` part from the URL.
:::note
It is possible to use:
- absolute slugs: `slug: /mySlug`, `slug: /`...
- relative slugs: `slug: mySlug`, `slug: ./../mySlug`...
:::
If you want a document to be available at the root, and have a path like `https://docusaurus.io/docs/`, you can use the slug front matter:
```md
---
id: my-home-doc
slug: /
---
Lorem ipsum
```
### Sidebars {#sidebars}
When using [autogenerated sidebars](./sidebar/autogenerated.mdx), the file structure will determine the sidebar structure.
Our recommendation for file system organization is: make your file system mirror the sidebar structure (so you don't need to handwrite your `sidebars.js` file), and use the `slug` front matter to customize URLs of each document.

View file

@ -0,0 +1,120 @@
---
id: introduction
sidebar_label: Introduction
slug: /docs-introduction
---
# Docs Introduction
The docs feature provides users with a way to organize Markdown files in a hierarchical format.
:::info
Check the [Docs Plugin API Reference documentation](./../../api/plugins/plugin-content-docs.mdx) for an exhaustive list of options.
:::
Your site's documentation is organized by four levels, from lowest to highest:
1. Individual pages.
2. Sidebars.
3. Versions.
4. Plugin instances.
The guide will introduce them in that order: starting from [how individual pages can be configured](./docs-create-doc.mdx), to [how to create a sidebar or multiple ones](./sidebar/index.mdx), to [how to create and manage versions](./versioning.mdx), to [how to use multiple docs plugin instances](./docs-multi-instance.mdx).
## Docs-only mode {#docs-only-mode}
A freshly initialized Docusaurus site has the following structure:
```
example.com/ -> generated from `src/pages/index.js`
example.com/docs/intro -> generated from `docs/intro.md`
example.com/docs/tutorial-basics/... -> generated from `docs/tutorial-basics/...`
...
example.com/blog/2021/08/26/welcome -> generated from `blog/2021-08-26-welcome/index.md`
example.com/blog/2021/08/01/mdx-blog-post -> generated from `blog/2021-08-01-mdx-blog-post.mdx`
...
```
All docs will be served under the subroute `docs/`. But what if **your site only has docs**, or you want to prioritize your docs by putting them at the root?
Assume that you have the following in your configuration:
```js title="docusaurus.config.js"
export default {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
/* docs plugin options */
},
blog: {
/* blog plugin options */
},
// ...
},
],
],
};
```
To enter docs-only mode, change it to like this:
```js title="docusaurus.config.js"
export default {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
routeBasePath: '/', // Serve the docs at the site's root
/* other docs plugin options */
},
// highlight-next-line
blog: false, // Optional: disable the blog plugin
// ...
},
],
],
};
```
Note that you **don't necessarily have to give up on using the blog** or other plugins; all that `routeBasePath: '/'` does is that instead of serving the docs through `https://example.com/docs/some-doc`, they are now at the site root: `https://example.com/some-doc`. The blog, if enabled, can still be accessed through the `blog/` subroute.
Don't forget to put some page at the root (`https://example.com/`) through adding the front matter:
```md title="docs/intro.md"
---
# highlight-next-line
slug: /
---
This page will be the home page when users visit https://example.com/.
```
:::warning
If you added `slug: /` to a doc to make it the homepage, you should delete the existing homepage at `./src/pages/index.js`, or else there will be two files mapping to the same route!
:::
Now, the site's structure will be like the following:
```
example.com/ -> generated from `docs/intro.md`
example.com/tutorial-basics/... -> generated from `docs/tutorial-basics/...`
...
```
:::tip
There's also a "blog-only mode" for those who only want to use the blog feature of Docusaurus. You can use the same method detailed above. Follow the setup instructions on [Blog-only mode](../../blog.mdx#blog-only-mode).
:::

View file

@ -0,0 +1,213 @@
---
id: multi-instance
description: Use multiple docs plugin instances on a single Docusaurus site.
slug: /docs-multi-instance
---
# Docs Multi-instance
The `@docusaurus/plugin-content-docs` plugin can support [multi-instance](../../using-plugins.mdx#multi-instance-plugins-and-plugin-ids).
:::note
This feature is only useful for [versioned documentation](./versioning.mdx). It is recommended to be familiar with docs versioning before reading this page. If you just want [multiple sidebars](./sidebar/multiple-sidebars.mdx), you can do so within one plugin.
:::
## Use-cases {#use-cases}
Sometimes you want a Docusaurus site to host 2 distinct sets of documentation (or more).
These documentations may even have different versioning/release lifecycles.
### Mobile SDKs documentation {#mobile-sdks-documentation}
If you build a cross-platform mobile SDK, you may have 2 documentations:
- Android SDK documentation (`v1.0`, `v1.1`)
- iOS SDK documentation (`v1.0`, `v2.0`)
In this case, you can use a distinct docs plugin instance per mobile SDK documentation.
:::warning
If each documentation instance is very large, you should rather create 2 distinct Docusaurus sites.
If someone edits the iOS documentation, is it really useful to rebuild everything, including the whole Android documentation that did not change?
:::
### Versioned and unversioned doc {#versioned-and-unversioned-doc}
Sometimes, you want some documents to be versioned, while other documents are more "global", and it feels useless to version them.
We use this pattern on the Docusaurus website itself:
- The [/docs/\*](/docs) section is versioned
- The [/community/\*](/community/support) section is unversioned
## Setup {#setup}
Suppose you have 2 documentations:
- Product: some versioned doc about your product
- Community: some unversioned doc about the community around your product
In this case, you should use the same plugin twice in your site configuration.
:::warning
`@docusaurus/preset-classic` already includes a docs plugin instance for you!
:::
When using the preset:
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-start
// id: 'product', // omitted => default instance
// highlight-end
path: 'product',
routeBasePath: 'product',
sidebarPath: './sidebarsProduct.js',
// ... other options
},
},
],
],
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
id: 'community',
// highlight-end
path: 'community',
routeBasePath: 'community',
sidebarPath: './sidebarsCommunity.js',
// ... other options
},
],
],
};
```
When not using the preset:
```js title="docusaurus.config.js"
export default {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
// id: 'product', // omitted => default instance
// highlight-end
path: 'product',
routeBasePath: 'product',
sidebarPath: './sidebarsProduct.js',
// ... other options
},
],
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
id: 'community',
// highlight-end
path: 'community',
routeBasePath: 'community',
sidebarPath: './sidebarsCommunity.js',
// ... other options
},
],
],
};
```
Don't forget to assign a unique `id` attribute to plugin instances.
:::note
We consider that the `product` instance is the most important one, and make it the "default" instance by not assigning any ID.
:::
## Versioned paths {#versioned-paths}
Each plugin instance will store versioned docs in a distinct folder.
The default plugin instance will use these paths:
- `website/versions.json`
- `website/versioned_docs`
- `website/versioned_sidebars`
The other plugin instances (with an `id` attribute) will use these paths:
- `website/[pluginId]_versions.json`
- `website/[pluginId]_versioned_docs`
- `website/[pluginId]_versioned_sidebars`
:::tip
You can omit the `id` attribute (defaults to `default`) for one of the docs plugin instances.
The instance paths will be simpler, and retro-compatible with a single-instance setup.
:::
## Tagging new versions {#tagging-new-versions}
Each plugin instance will have its own CLI command to tag a new version. They will be displayed if you run:
```bash npm2yarn
npm run docusaurus -- --help
```
To version the product/default docs plugin instance:
```bash npm2yarn
npm run docusaurus docs:version 1.0.0
```
To version the non-default/community docs plugin instance:
```bash npm2yarn
npm run docusaurus docs:version:community 1.0.0
```
## Docs navbar items {#docs-navbar-items}
Each docs-related [theme navbar items](../../api/themes/theme-configuration.mdx#navbar) take an optional `docsPluginId` attribute.
For example, if you want to have one version dropdown for each mobile SDK (iOS and Android), you could do:
```js title="docusaurus.config.js"
export default {
themeConfig: {
navbar: {
items: [
{
type: 'docsVersionDropdown',
// highlight-start
docsPluginId: 'ios',
// highlight-end
},
{
type: 'docsVersionDropdown',
// highlight-start
docsPluginId: 'android',
// highlight-end
},
],
},
},
};
```

View file

@ -0,0 +1,497 @@
---
slug: /sidebar/autogenerated
---
# Autogenerated
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
Docusaurus can **create a sidebar automatically** from your **filesystem structure**: each folder creates a sidebar category, and each file creates a doc link.
```ts
type SidebarItemAutogenerated = {
type: 'autogenerated';
dirName: string; // Source folder to generate the sidebar slice from (relative to docs)
};
```
Docusaurus can generate a full sidebar from your docs folder:
```js title="sidebars.js"
export default {
myAutogeneratedSidebar: [
// highlight-start
{
type: 'autogenerated',
dirName: '.', // '.' means the current docs folder
},
// highlight-end
],
};
```
An `autogenerated` item is converted by Docusaurus to a **sidebar slice** (also discussed in [category shorthands](items.mdx#category-shorthand)): a list of items of type `doc` or `category`, so you can splice **multiple `autogenerated` items** from multiple directories, interleaving them with regular sidebar items, in one sidebar level.
<details>
<summary>A real-world example</summary>
Consider this file structure:
```bash
docs
├── api
│ ├── product1-api
│ │ └── api.md
│ └── product2-api
│ ├── basic-api.md
│ └── pro-api.md
├── intro.md
└── tutorials
├── advanced
│ ├── advanced1.md
│ ├── advanced2.md
│ └── read-more
│ ├── resource1.md
│ └── resource2.md
├── easy
│ ├── easy1.md
│ └── easy2.md
├── tutorial-end.md
├── tutorial-intro.md
└── tutorial-medium.md
```
And assume every doc's ID is just its file name. If you define an autogenerated sidebar like this:
```js title="sidebars.js"
export default {
mySidebar: [
'intro',
{
type: 'category',
label: 'Tutorials',
items: [
'tutorial-intro',
// highlight-start
{
type: 'autogenerated',
dirName: 'tutorials/easy', // Generate sidebar slice from docs/tutorials/easy
},
// highlight-end
'tutorial-medium',
// highlight-start
{
type: 'autogenerated',
dirName: 'tutorials/advanced', // Generate sidebar slice from docs/tutorials/advanced
},
// highlight-end
'tutorial-end',
],
},
// highlight-start
{
type: 'autogenerated',
dirName: 'api', // Generate sidebar slice from docs/api
},
// highlight-end
{
type: 'category',
label: 'Community',
items: ['team', 'chat'],
},
],
};
```
It would be resolved as:
```js title="sidebars.js"
export default {
mySidebar: [
'intro',
{
type: 'category',
label: 'Tutorials',
items: [
'tutorial-intro',
// highlight-start
// Two files in docs/tutorials/easy
'easy1',
'easy2',
// highlight-end
'tutorial-medium',
// highlight-start
// Two files and a folder in docs/tutorials/advanced
'advanced1',
'advanced2',
{
type: 'category',
label: 'read-more',
items: ['resource1', 'resource2'],
},
// highlight-end
'tutorial-end',
],
},
// highlight-start
// Two folders in docs/api
{
type: 'category',
label: 'product1-api',
items: ['api'],
},
{
type: 'category',
label: 'product2-api',
items: ['basic-api', 'pro-api'],
},
// highlight-end
{
type: 'category',
label: 'Community',
items: ['team', 'chat'],
},
],
};
```
Note how the autogenerate source directories themselves don't become categories: only the items they contain do. This is what we mean by "sidebar slice".
</details>
## Category index convention {#category-index-convention}
Docusaurus can automatically link a category to its index document.
A category index document is a document following one of those filename conventions:
- Named as `index` (case-insensitive): `docs/Guides/index.md`
- Named as `README` (case-insensitive): `docs/Guides/README.mdx`
- Same name as parent folder: `docs/Guides/Guides.md`
This is equivalent to using a category with a [doc link](items.mdx#category-doc-link):
```js title="sidebars.js"
export default {
docs: [
// highlight-start
{
type: 'category',
label: 'Guides',
link: {type: 'doc', id: 'Guides/index'},
items: [],
},
// highlight-end
],
};
```
:::tip
Naming your introductory document `README.md` makes it show up when browsing the folder using the GitHub interface, while using `index.md` makes the behavior more in line with how HTML files are served.
:::
:::tip
If a folder only has one index page, it will be turned into a link instead of a category. This is useful for **asset collocation**:
```
some-doc
├── index.md
├── img1.png
└── img2.png
```
:::
<details>
<summary>Customizing category index matching</summary>
It is possible to opt out any of the category index conventions, or define even more conventions. You can inject your own `isCategoryIndex` matcher through the [`sidebarItemsGenerator`](#customize-the-sidebar-items-generator) callback. For example, you can also pick `intro` as another file name eligible for automatically becoming the category index.
```js title="docusaurus.config.js"
export default {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
async sidebarItemsGenerator({
...args,
isCategoryIndex: defaultCategoryIndexMatcher, // The default matcher implementation, given below
defaultSidebarItemsGenerator,
}) {
return defaultSidebarItemsGenerator({
...args,
// highlight-start
isCategoryIndex(doc) {
return (
// Also pick intro.md in addition to the default ones
doc.fileName.toLowerCase() === 'intro' ||
defaultCategoryIndexMatcher(doc)
);
},
// highlight-end
});
},
},
],
],
};
```
Or choose to not have any category index convention.
```js title="docusaurus.config.js"
export default {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
async sidebarItemsGenerator({
...args,
isCategoryIndex: defaultCategoryIndexMatcher, // The default matcher implementation, given below
defaultSidebarItemsGenerator,
}) {
return defaultSidebarItemsGenerator({
...args,
// highlight-start
isCategoryIndex() {
// No doc will be automatically picked as category index
return false;
},
// highlight-end
});
},
},
],
],
};
```
The `isCategoryIndex` matcher will be provided with three fields:
- `fileName`, the file's name without extension and with casing preserved
- `directories`, the list of directory names _from the lowest level to the highest level_, relative to the docs root directory
- `extension`, the file's extension, with a leading dot.
For example, for a doc file at `guides/sidebar/autogenerated.md`, the props the matcher receives are
```js
const props = {
fileName: 'autogenerated',
directories: ['sidebar', 'guides'],
extension: '.md',
};
```
The default implementation is:
```js
function isCategoryIndex({fileName, directories}) {
const eligibleDocIndexNames = [
'index',
'readme',
directories[0].toLowerCase(),
];
return eligibleDocIndexNames.includes(fileName.toLowerCase());
}
```
</details>
## Autogenerated sidebar metadata {#autogenerated-sidebar-metadata}
For handwritten sidebar definitions, you would provide metadata to sidebar items through `sidebars.js`; for autogenerated, Docusaurus would read them from the item's respective file. In addition, you may want to adjust the relative position of each item because, by default, items within a sidebar slice will be generated in **alphabetical order** (using file and folder names).
### Doc item metadata {#doc-item-metadata}
The `label`, `className`, and `customProps` attributes are declared in front matter as `sidebar_label`, `sidebar_class_name`, and `sidebar_custom_props`, respectively. Position can be specified in the same way, via `sidebar_position` front matter.
```md title="docs/tutorials/tutorial-easy.md"
---
# highlight-start
sidebar_position: 2
sidebar_label: Easy
sidebar_class_name: green
# highlight-end
---
# Easy Tutorial
This is the easy tutorial!
```
### Category item metadata {#category-item-metadata}
Add a `_category_.json` or `_category_.yml` file in the respective folder. You can specify any category metadata and also the `position` metadata. `label`, `className`, `position`, and `customProps` will default to the respective values of the category's linked doc, if there is one.
<Tabs>
<TabItem value="JSON">
```json title="docs/tutorials/_category_.json"
{
"position": 2.5,
"label": "Tutorial",
"collapsible": true,
"collapsed": false,
"className": "red",
"link": {
"type": "generated-index",
"title": "Tutorial overview"
},
"customProps": {
"description": "This description can be used in the swizzled DocCard"
}
}
```
</TabItem>
<TabItem value="YAML">
```yml title="docs/tutorials/_category_.yml"
position: 2.5 # float position is supported
label: 'Tutorial'
collapsible: true # make the category collapsible
collapsed: false # keep the category open by default
className: red
link:
type: generated-index
title: Tutorial overview
customProps:
description: This description can be used in the swizzled DocCard
```
</TabItem>
</Tabs>
:::info
If the `link` is explicitly specified, Docusaurus will not apply any [default conventions](#category-index-convention).
The doc links can be specified relatively, e.g. if the category is generated with the `guides` directory, `"link": {"type": "doc", "id": "intro"}` will be resolved to the ID `guides/intro`, only falling back to `intro` if a doc with the former ID doesn't exist.
You can also use `link: null` to opt out of default conventions and not generate any category index page.
:::
:::info
The position metadata is only used **within a sidebar slice**: Docusaurus does not re-order other items of your sidebar.
:::
## Using number prefixes {#using-number-prefixes}
A simple way to order an autogenerated sidebar is to prefix docs and folders by number prefixes, which also makes them appear in the file system in the same order when sorted by file name:
```bash
docs
├── 01-Intro.md
├── 02-Tutorial Easy
│ ├── 01-First Part.md
│ ├── 02-Second Part.md
│ └── 03-End.md
├── 03-Tutorial Advanced
│ ├── 01-First Part.md
│ ├── 02-Second Part.md
│ ├── 03-Third Part.md
│ └── 04-End.md
└── 04-End.md
```
To make it **easier to adopt**, Docusaurus supports **multiple number prefix patterns**.
By default, Docusaurus will **remove the number prefix** from the doc id, title, label, and URL paths.
:::warning
**Prefer using [additional metadata](#autogenerated-sidebar-metadata)**.
Updating a number prefix can be annoying, as it can require **updating multiple existing Markdown links**:
```diff title="docs/02-Tutorial Easy/01-First Part.md"
- Check the [Tutorial End](../04-End.mdx);
+ Check the [Tutorial End](../05-End.mdx);
```
:::
## Customize the sidebar items generator {#customize-the-sidebar-items-generator}
You can provide a custom `sidebarItemsGenerator` function in the docs plugin (or preset) config:
```js title="docusaurus.config.js"
export default {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
async sidebarItemsGenerator({
defaultSidebarItemsGenerator,
numberPrefixParser,
item,
version,
docs,
categoriesMetadata,
isCategoryIndex,
}) {
// Example: return an hardcoded list of static sidebar items
return [
{type: 'doc', id: 'doc1'},
{type: 'doc', id: 'doc2'},
];
},
// highlight-end
},
],
],
};
```
:::tip
**Re-use and enhance the default generator** instead of writing a generator from scratch: [the default generator we provide](https://github.com/facebook/docusaurus/blob/main/packages/docusaurus-plugin-content-docs/src/sidebars/generator.ts) is 250 lines long.
**Add, update, filter, re-order** the sidebar items according to your use case:
```js title="docusaurus.config.js"
// highlight-start
// Reverse the sidebar items ordering (including nested category items)
function reverseSidebarItems(items) {
// Reverse items in categories
const result = items.map((item) => {
if (item.type === 'category') {
return {...item, items: reverseSidebarItems(item.items)};
}
return item;
});
// Reverse items at current level
result.reverse();
return result;
}
// highlight-end
export default {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
async sidebarItemsGenerator({defaultSidebarItemsGenerator, ...args}) {
const sidebarItems = await defaultSidebarItemsGenerator(args);
return reverseSidebarItems(sidebarItems);
},
// highlight-end
},
],
],
};
```
:::

View file

@ -0,0 +1,212 @@
---
slug: /sidebar
---
# Sidebar
Creating a sidebar is useful to:
- Group multiple **related documents**
- **Display a sidebar** on each of those documents
- Provide **paginated navigation**, with next/previous button
To use sidebars on your Docusaurus site:
1. Define a file that exports a dictionary of [sidebar objects](#sidebar-object).
2. Pass this object into the `@docusaurus/plugin-docs` plugin directly or via `@docusaurus/preset-classic`.
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
sidebarPath: './sidebars.js',
},
},
],
],
};
```
This section serves as an overview of miscellaneous features of the doc sidebar. In the following sections, we will more systematically introduce the following concepts:
```mdx-code-block
import DocCardList from '@theme/DocCardList';
<DocCardList />
```
## Default sidebar {#default-sidebar}
If the `sidebarPath` is unspecified, Docusaurus [automatically generates a sidebar](autogenerated.mdx) for you, by using the filesystem structure of the `docs` folder:
```js title="sidebars.js"
export default {
mySidebar: [
{
type: 'autogenerated',
dirName: '.', // generate sidebar from the docs folder (or versioned_docs/<version>)
},
],
};
```
You can also define your sidebars explicitly.
## Sidebar object {#sidebar-object}
A sidebar at its crux is a hierarchy of categories, doc links, and other hyperlinks.
```ts
type Sidebar =
// Normal syntax
| SidebarItem[]
// Shorthand syntax
| {[categoryLabel: string]: SidebarItem[]};
```
For example:
```js title="sidebars.js"
export default {
mySidebar: [
{
type: 'category',
label: 'Getting Started',
items: [
{
type: 'doc',
id: 'doc1',
},
],
},
{
type: 'category',
label: 'Docusaurus',
items: [
{
type: 'doc',
id: 'doc2',
},
{
type: 'doc',
id: 'doc3',
},
],
},
{
type: 'link',
label: 'Learn more',
href: 'https://example.com',
},
],
};
```
This is a sidebars file that exports one sidebar, called `mySidebar`. It has three top-level items: two categories and one external link. Within each category, there are a few doc links.
A sidebars file can contain [**multiple sidebar objects**](multiple-sidebars.mdx), identified by their object keys.
```ts
type SidebarsFile = {
[sidebarID: string]: Sidebar;
};
```
## Theme configuration {#theme-configuration}
### Hideable sidebar {#hideable-sidebar}
By enabling the `themeConfig.docs.sidebar.hideable` option, you can make the entire sidebar hideable, allowing users to better focus on the content. This is especially useful when content is consumed on medium-sized screens (e.g. tablets).
```js title="docusaurus.config.js"
export default {
themeConfig: {
// highlight-start
docs: {
sidebar: {
hideable: true,
},
},
// highlight-end
},
};
```
### Auto-collapse sidebar categories {#auto-collapse-sidebar-categories}
The `themeConfig.docs.sidebar.autoCollapseCategories` option would collapse all sibling categories when expanding one category. This saves the user from having too many categories open and helps them focus on the selected section.
```js title="docusaurus.config.js"
export default {
themeConfig: {
// highlight-start
docs: {
sidebar: {
autoCollapseCategories: true,
},
},
// highlight-end
},
};
```
## Passing custom props {#passing-custom-props}
To pass in custom props to a sidebar item, add the optional `customProps` object to any of the items. This is useful to apply site customizations by swizzling React components rendering sidebar items.
```js
{
type: 'doc',
id: 'doc1',
// highlight-start
customProps: {
badges: ['new', 'green'],
featured: true,
},
// highlight-end
};
```
## Sidebar Breadcrumbs {#sidebar-breadcrumbs}
By default, breadcrumbs are rendered at the top, using the "sidebar path" of the current page.
This behavior can be disabled with plugin options:
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
breadcrumbs: false,
},
},
],
],
};
```
## Complex sidebars example {#complex-sidebars-example}
A real-world example from the Docusaurus site:
```mdx-code-block
import CodeBlock from '@theme/CodeBlock';
<CodeBlock language="js" title="sidebars.js">
{require('!!raw-loader!@site/sidebars.ts')
.default
.split('\n')
// remove comments
.map((line) => !['//','/*','*'].some(commentPattern => line.trim().startsWith(commentPattern)) && line)
.filter(Boolean)
.join('\n')}
</CodeBlock>
```

View file

@ -0,0 +1,619 @@
---
toc_max_heading_level: 4
slug: /sidebar/items
---
# Sidebar items
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import BrowserWindow from '@site/src/components/BrowserWindow';
```
We have introduced three types of item types in the example in the previous section: `doc`, `category`, and `link`, whose usages are fairly intuitive. We will formally introduce their APIs. There's also a fourth type: `autogenerated`, which we will explain in detail later.
- **[Doc](#sidebar-item-doc)**: link to a doc page, associating it with the sidebar
- **[Link](#sidebar-item-link)**: link to any internal or external page
- **[Category](#sidebar-item-category)**: creates a dropdown of sidebar items
- **[Autogenerated](autogenerated.mdx)**: generate a sidebar slice automatically
- **[HTML](#sidebar-item-html)**: renders pure HTML in the item's position
- **[\*Ref](multiple-sidebars.mdx#sidebar-item-ref)**: link to a doc page, without making the item take part in navigation generation
## Doc: link to a doc {#sidebar-item-doc}
Use the `doc` type to link to a doc page and assign that doc to a sidebar:
```ts
type SidebarItemDoc =
// Normal syntax
| {
type: 'doc';
id: string;
label: string; // Sidebar label text
className?: string; // Class name for sidebar label
customProps?: Record<string, unknown>; // Custom props
}
// Shorthand syntax
| string; // docId shortcut
```
Example:
```js title="sidebars.js"
export default {
mySidebar: [
// Normal syntax:
// highlight-start
{
type: 'doc',
id: 'doc1', // document ID
label: 'Getting started', // sidebar label
},
// highlight-end
// Shorthand syntax:
// highlight-start
'doc2', // document ID
// highlight-end
],
};
```
If you use the doc shorthand or [autogenerated](autogenerated.mdx) sidebar, you would lose the ability to customize the sidebar label through item definition. You can, however, use the `sidebar_label` Markdown front matter within that doc, which has higher precedence over the `label` key in the sidebar item. Similarly, you can use `sidebar_custom_props` to declare custom metadata for a doc page.
:::note
A `doc` item sets an [implicit sidebar association](./multiple-sidebars.mdx#sidebar-association). Don't assign the same doc to multiple sidebars: change the type to `ref` instead.
:::
:::tip
Sidebar custom props is a useful way to propagate arbitrary doc metadata to the client side, so you can get additional information when using any doc-related hook that fetches a doc object.
:::
## Link: link to any page {#sidebar-item-link}
Use the `link` type to link to any page (internal or external) that is not a doc.
```ts
type SidebarItemLink = {
type: 'link';
label: string;
href: string;
className?: string;
description?: string;
};
```
Example:
```js title="sidebars.js"
export default {
myLinksSidebar: [
// highlight-start
// External link
{
type: 'link',
label: 'Facebook', // The link label
href: 'https://facebook.com', // The external URL
},
// highlight-end
// highlight-start
// Internal link
{
type: 'link',
label: 'Home', // The link label
href: '/', // The internal path
},
// highlight-end
],
};
```
## HTML: render custom markup {#sidebar-item-html}
Use the `html` type to render custom HTML within the item's `<li>` tag.
This can be useful for inserting custom items such as dividers, section titles, ads, and images.
```ts
type SidebarItemHtml = {
type: 'html';
value: string;
defaultStyle?: boolean; // Use default menu item styles
className?: string;
};
```
Example:
```js title="sidebars.js"
export default {
myHtmlSidebar: [
// highlight-start
{
type: 'html',
value: '<img src="sponsor.png" alt="Sponsor" />', // The HTML to be rendered
defaultStyle: true, // Use the default menu item styling
},
// highlight-end
],
};
```
:::tip
The menu item is already wrapped in an `<li>` tag, so if your custom item is simple, such as a title, just supply a string as the value and use the `className` property to style it:
```js title="sidebars.js"
export default {
myHtmlSidebar: [
{
type: 'html',
value: 'Core concepts',
className: 'sidebar-title',
},
],
};
```
:::
## Category: create a hierarchy {#sidebar-item-category}
Use the `category` type to create a hierarchy of sidebar items.
```ts
type SidebarItemCategory = {
type: 'category';
label: string; // Sidebar label text.
items: SidebarItem[]; // Array of sidebar items.
className?: string;
description?: string;
// Category options:
collapsible: boolean; // Set the category to be collapsible
collapsed: boolean; // Set the category to be initially collapsed or open by default
link: SidebarItemCategoryLinkDoc | SidebarItemCategoryLinkGeneratedIndex;
};
```
Example:
```js title="sidebars.js"
export default {
docs: [
{
type: 'category',
label: 'Guides',
collapsible: true,
collapsed: false,
items: [
'creating-pages',
{
type: 'category',
label: 'Docs',
items: ['introduction', 'sidebar', 'markdown-features', 'versioning'],
},
],
},
],
};
```
:::tip
Use the [**shorthand syntax**](#category-shorthand) when you don't need customizations:
```js title="sidebars.js"
export default {
docs: {
Guides: [
'creating-pages',
{
Docs: ['introduction', 'sidebar', 'markdown-features', 'versioning'],
},
],
},
};
```
:::
### Category links {#category-link}
With category links, clicking on a category can navigate you to another page.
:::tip
Use category links to introduce a category of documents.
Autogenerated categories can use the [`_category_.yml`](./autogenerated.mdx#category-item-metadata) file to declare the link.
:::
#### Generated index page {#generated-index-page}
You can auto-generate an index page that displays all the direct children of this category. The `slug` allows you to customize the generated page's route, which defaults to `/category/[categoryName]`.
```js title="sidebars.js"
export default {
docs: [
{
type: 'category',
label: 'Guides',
// highlight-start
link: {
type: 'generated-index',
title: 'Docusaurus Guides',
description: 'Learn about the most important Docusaurus concepts!',
slug: '/category/docusaurus-guides',
keywords: ['guides'],
image: '/img/docusaurus.png',
},
// highlight-end
items: ['pages', 'docs', 'blog', 'search'],
},
],
};
```
See it in action on the [Docusaurus Guides page](/docs/category/guides).
:::tip
Use `generated-index` links as a quick way to get an introductory document.
:::
#### Doc link {#category-doc-link}
A category can link to an existing document.
```js title="sidebars.js"
export default {
docs: [
{
type: 'category',
label: 'Guides',
// highlight-start
link: {type: 'doc', id: 'introduction'},
// highlight-end
items: ['pages', 'docs', 'blog', 'search'],
},
],
};
```
See it in action on the [i18n introduction page](../../../i18n/i18n-introduction.mdx).
#### Embedding generated index in doc page {#embedding-generated-index-in-doc-page}
You can embed the generated cards list in a normal doc page as well with the `DocCardList` component. It will display all the sidebar items of the parent category of the current document.
```md title="docs/sidebar/index.md"
import DocCardList from '@theme/DocCardList';
<DocCardList />
```
```mdx-code-block
<BrowserWindow>
import DocCardList from '@theme/DocCardList';
<DocCardList />
</BrowserWindow>
```
### Collapsible categories {#collapsible-categories}
We support the option to expand/collapse categories. Categories are collapsible by default, but you can disable collapsing with `collapsible: false`.
```js title="sidebars.js"
export default {
docs: [
{
type: 'category',
label: 'Guides',
items: [
'creating-pages',
{
type: 'category',
// highlight-next-line
collapsible: false,
label: 'Docs',
items: ['introduction', 'sidebar', 'markdown-features', 'versioning'],
},
],
},
],
};
```
To make all categories non-collapsible by default, set the `sidebarCollapsible` option in `plugin-content-docs` to `false`:
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
sidebarCollapsible: false,
},
},
],
],
};
```
:::note
The option in `sidebars.js` takes precedence over plugin configuration, so it is possible to make certain categories collapsible when `sidebarCollapsible` is set to `false` globally.
:::
### Expanded categories by default {#expanded-categories-by-default}
Collapsible categories are collapsed by default. If you want them to be expanded on the first render, you can set `collapsed` to `false`:
```js title="sidebars.js"
export default {
docs: {
Guides: [
'creating-pages',
{
type: 'category',
label: 'Docs',
// highlight-next-line
collapsed: false,
items: ['markdown-features', 'sidebar', 'versioning'],
},
],
},
};
```
Similar to `collapsible`, you can also set the global configuration `options.sidebarCollapsed` to `false`. Individual `collapsed` options in `sidebars.js` will still take precedence over this configuration.
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
sidebarCollapsed: false,
},
},
],
],
};
```
:::warning
When a category has `collapsed: true` but `collapsible: false` (either through `sidebars.js` or through plugin configuration), the latter takes precedence and the category is still rendered as expanded.
:::
## Using shorthands {#using-shorthands}
You can express typical sidebar items without much customization more concisely with **shorthand syntaxes**. There are two parts to this: [**doc shorthand**](#doc-shorthand) and [**category shorthand**](#category-shorthand).
### Doc shorthand {#doc-shorthand}
An item with type `doc` can be simply a string representing its ID:
```mdx-code-block
<Tabs>
<TabItem value="Longhand">
```
```js title="sidebars.js"
export default {
sidebar: [
// highlight-start
{
type: 'doc',
id: 'myDoc',
},
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="Shorthand">
```
```js title="sidebars.js"
export default {
sidebar: [
// highlight-start
'myDoc',
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
</Tabs>
```
So it's possible to simplify the example above to:
```js title="sidebars.js"
export default {
mySidebar: [
{
type: 'category',
label: 'Getting Started',
items: [
// highlight-next-line
'doc1',
],
},
{
type: 'category',
label: 'Docusaurus',
items: [
// highlight-start
'doc2',
'doc3',
// highlight-end
],
},
{
type: 'link',
label: 'Learn more',
href: 'https://example.com',
},
],
};
```
### Category shorthand {#category-shorthand}
A category item can be represented by an object whose key is its label, and the value is an array of subitems.
```mdx-code-block
<Tabs>
<TabItem value="Longhand">
```
```js title="sidebars.js"
export default {
sidebar: [
// highlight-start
{
type: 'category',
label: 'Getting started',
items: ['doc1', 'doc2'],
},
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="Shorthand">
```
```js title="sidebars.js"
export default {
sidebar: [
// highlight-start
{
'Getting started': ['doc1', 'doc2'],
},
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
</Tabs>
```
This permits us to simplify that example to:
```js title="sidebars.js"
export default {
mySidebar: [
// highlight-start
{
'Getting started': ['doc1'],
},
{
Docusaurus: ['doc2', 'doc3'],
},
// highlight-end
{
type: 'link',
label: 'Learn more',
href: 'https://example.com',
},
],
};
```
Each shorthand object after this transformation will contain exactly one entry. Now consider the further simplified example below:
```js title="sidebars.js"
export default {
mySidebar: [
// highlight-start
{
'Getting started': ['doc1'],
Docusaurus: ['doc2', 'doc3'],
},
// highlight-end
{
type: 'link',
label: 'Learn more',
href: 'https://example.com',
},
],
};
```
Note how the two consecutive category shorthands are compressed into one object with two entries. This syntax generates a **sidebar slice**: you shouldn't see that object as one bulk item—this object is unwrapped, with each entry becoming a separate item, and they spliced together with the rest of the items (in this case, the "Learn more" link) to form the final sidebar level. Sidebar slices are also important when discussing [autogenerated sidebars](autogenerated.mdx).
Wherever you have an array of items that is reduced to one category shorthand, you can omit that enclosing array as well.
```mdx-code-block
<Tabs>
<TabItem value="Before">
```
```js title="sidebars.js"
export default {
sidebar: [
{
'Getting started': ['doc1'],
Docusaurus: [
{
'Basic guides': ['doc2', 'doc3'],
'Advanced guides': ['doc4', 'doc5'],
},
],
},
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="After">
```
```js title="sidebars.js"
export default {
sidebar: {
'Getting started': ['doc1'],
Docusaurus: {
'Basic guides': ['doc2', 'doc3'],
'Advanced guides': ['doc4', 'doc5'],
},
},
};
```
```mdx-code-block
</TabItem>
</Tabs>
```

View file

@ -0,0 +1,143 @@
---
slug: /sidebar/multiple-sidebars
---
# Using multiple sidebars
You can create a sidebar for each **set of Markdown files** that you want to **group together**.
:::tip
The Docusaurus site is a good example of using multiple sidebars:
- [Docs](../../../introduction.mdx)
- [API](../../../cli.mdx)
:::
Consider this example:
```js title="sidebars.js"
export default {
tutorialSidebar: {
'Category A': ['doc1', 'doc2'],
},
apiSidebar: ['doc3', 'doc4'],
};
```
When browsing `doc1` or `doc2`, the `tutorialSidebar` will be displayed; when browsing `doc3` or `doc4`, the `apiSidebar` will be displayed.
## Understanding sidebar association {#sidebar-association}
Following the example above, if a `commonDoc` is included in both sidebars:
```js title="sidebars.js"
export default {
tutorialSidebar: {
'Category A': ['doc1', 'doc2', 'commonDoc'],
},
apiSidebar: ['doc3', 'doc4', 'commonDoc'],
};
```
How does Docusaurus know which sidebar to display when browsing `commonDoc`? Answer: it doesn't, and we don't guarantee which sidebar it will pick.
When you add doc Y to sidebar X, it creates a two-way binding: sidebar X contains a link to doc Y, and when browsing doc Y, sidebar X will be displayed. But sometimes, we want to break either implicit binding:
1. _How do I generate a link to doc Y in sidebar X without making sidebar X displayed on Y?_ For example, when I include doc Y in multiple sidebars as in the example above, and I want to explicitly tell Docusaurus to display one sidebar?
2. _How do I make sidebar X displayed when browsing doc Y, but sidebar X shouldn't contain the link to Y?_ For example, when Y is a "doc home page" and the sidebar is purely used for navigation?
Front matter option `displayed_sidebar` will forcibly set the sidebar association. For the same example, you can still use doc shorthands without any special configuration:
```js title="sidebars.js"
export default {
tutorialSidebar: {
'Category A': ['doc1', 'doc2'],
},
apiSidebar: ['doc3', 'doc4'],
};
```
And then add a front matter:
```md title="commonDoc.md"
---
displayed_sidebar: apiSidebar
---
```
Which explicitly tells Docusaurus to display `apiSidebar` when browsing `commonDoc`. Using the same method, you can make sidebar X which doesn't contain doc Y appear on doc Y:
```md title="home.md"
---
displayed_sidebar: tutorialSidebar
---
```
Even when `tutorialSidebar` doesn't contain a link to `home`, it will still be displayed when viewing `home`.
If you set `displayed_sidebar: null`, no sidebar will be displayed whatsoever on this page, and subsequently, no pagination either.
## Generating pagination {#generating-pagination}
Docusaurus uses the sidebar to generate the "next" and "previous" pagination links at the bottom of each doc page. It strictly uses the sidebar that is displayed: if no sidebar is associated, it doesn't generate pagination either. However, the docs linked as "next" and "previous" are not guaranteed to display the same sidebar: they are included in this sidebar, but in their front matter, they may have a different `displayed_sidebar`.
If a sidebar is displayed by setting `displayed_sidebar` front matter, and this sidebar doesn't contain the doc itself, no pagination is displayed.
You can customize pagination with front matter `pagination_next` and `pagination_prev`. Consider this sidebar:
```js title="sidebars.js"
export default {
tutorial: [
'introduction',
{
installation: ['windows', 'linux', 'macos'],
},
'getting-started',
],
};
```
The pagination next link on "windows" points to "linux", but that doesn't make sense: you would want readers to proceed to "getting started" after installation. In this case, you can set the pagination manually:
```md title="windows.md"
---
# highlight-next-line
pagination_next: getting-started
---
# Installation on Windows
```
You can also disable displaying a pagination link with `pagination_next: null` or `pagination_prev: null`.
The pagination label by default is the sidebar label. You can use the front matter `pagination_label` to customize how this doc appears in the pagination.
## The `ref` item {#sidebar-item-ref}
The `ref` type is identical to the [`doc` type](./items.mdx#sidebar-item-doc) in every way, except that it doesn't participate in generating navigation metadata. It only registers itself as a link. When [generating pagination](#generating-pagination) and [displaying sidebar](#sidebar-association), `ref` items are completely ignored.
It is particularly useful where you wish to link to the same document from multiple sidebars. The document only belongs to one sidebar (the one where it's registered as `type: 'doc'` or from an autogenerated directory), but its link will appear in all sidebars that it's registered in.
Consider this example:
```js title="sidebars.js"
export default {
tutorialSidebar: {
'Category A': [
'doc1',
'doc2',
// highlight-next-line
{type: 'ref', id: 'commonDoc'},
'doc5',
],
},
apiSidebar: ['doc3', 'doc4', 'commonDoc'],
};
```
You can think of the `ref` type as the equivalent to doing the following:
- Setting `displayed_sidebar: tutorialSidebar` for `commonDoc` (`ref` is ignored in sidebar association)
- Setting `pagination_next: doc5` for `doc2` and setting `pagination_prev: doc2` for `doc5` (`ref` is ignored in pagination generation)

View file

@ -0,0 +1,329 @@
---
slug: /versioning
---
# Versioning
You can use the versioning CLI to create a new documentation version based on the latest content in the `docs` directory. That specific set of documentation will then be preserved and accessible even as the documentation in the `docs` directory continues to evolve.
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
:::warning
Think about it before starting to version your documentation - it can become difficult for contributors to help improve it!
:::
Most of the time, you don't need versioning as it will just increase your build time, and introduce complexity to your codebase. Versioning is **best suited for websites with high-traffic and rapid changes to documentation between versions**. If your documentation rarely changes, don't add versioning to your documentation.
To better understand how versioning works and see if it suits your needs, you can read on below.
## Overview {#overview}
A typical versioned doc site looks like below:
```bash
website
├── sidebars.json # sidebar for the current docs version
├── docs # docs directory for the current docs version
│ ├── foo
│ │ └── bar.md # https://mysite.com/docs/next/foo/bar
│ └── hello.md # https://mysite.com/docs/next/hello
├── versions.json # file to indicate what versions are available
├── versioned_docs
│ ├── version-1.1.0
│ │ ├── foo
│ │ │ └── bar.md # https://mysite.com/docs/foo/bar
│ │ └── hello.md
│ └── version-1.0.0
│ ├── foo
│ │ └── bar.md # https://mysite.com/docs/1.0.0/foo/bar
│ └── hello.md
├── versioned_sidebars
│ ├── version-1.1.0-sidebars.json
│ └── version-1.0.0-sidebars.json
├── docusaurus.config.js
└── package.json
```
The `versions.json` file is a list of version names, ordered from newest to oldest.
The table below explains how a versioned file maps to its version and the generated URL.
| Path | Version | URL |
| --------------------------------------- | -------------- | ----------------- |
| `versioned_docs/version-1.0.0/hello.md` | 1.0.0 | /docs/1.0.0/hello |
| `versioned_docs/version-1.1.0/hello.md` | 1.1.0 (latest) | /docs/hello |
| `docs/hello.md` | current | /docs/next/hello |
:::tip
The files in the `docs` directory belong to the `current` docs version.
By default, the `current` docs version is labeled as `Next` and hosted under `/docs/next/*`, but it is entirely configurable to fit your project's release lifecycle.
:::
### Terminology {#terminology}
Note the terminology we use here.
<dl>
<dt>
<b>Current version</b>
</dt>
<dd>
{'The version placed in the '}
<code>./docs</code>
{' folder.'}
</dd>
<dt>
<b>Latest version / last version</b>
</dt>
<dd>
{'The version served by default for docs navbar items. Usually has path '}
<code>/docs</code>
{'.'}
</dd>
</dl>
Current version is defined by the **file system location**, while latest version is defined by the **the navigation behavior**. They may or may not be the same version! (And the default configuration, as shown in the table above, would treat them as different: current version at `/docs/next` and latest at `/docs`.)
## Tutorials {#tutorials}
### Tagging a new version {#tagging-a-new-version}
1. First, make sure the current docs version (the `./docs` directory) is ready to be frozen.
2. Enter a new version number.
```bash npm2yarn
npm run docusaurus docs:version 1.1.0
```
When tagging a new version, the document versioning mechanism will:
- Copy the full `docs/` folder contents into a new `versioned_docs/version-[versionName]/` folder.
- Create a versioned sidebars file based from your current [sidebar](./sidebar/index.mdx) configuration (if it exists) - saved as `versioned_sidebars/version-[versionName]-sidebars.json`.
- Append the new version number to `versions.json`.
### Creating new docs {#creating-new-docs}
1. Place the new file into the corresponding version folder.
2. Include the reference to the new file in the corresponding sidebar file according to the version number.
```mdx-code-block
<Tabs>
<TabItem value="Current version structure">
```
```bash
# The new file.
docs/new.md
# Edit the corresponding sidebar file.
sidebars.js
```
```mdx-code-block
</TabItem>
<TabItem value="Older version structure">
```
```bash
# The new file.
versioned_docs/version-1.0.0/new.md
# Edit the corresponding sidebar file.
versioned_sidebars/version-1.0.0-sidebars.json
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::tip
Versioned sidebar files are, like standard sidebar files, relative to the content root for the given version — so for the example above, your versioned sidebar file may look like:
```json
{
"sidebar": [
{
"type": "autogenerated",
"dirName": "."
}
]
}
```
or for a manual sidebar:
```json
{
"sidebar": [
{
"type": "doc",
"id": "new",
"label": "New"
}
]
}
```
:::
### Updating an existing version {#updating-an-existing-version}
You can update multiple docs versions at the same time because each directory in `versioned_docs/` represents specific routes when published.
1. Edit any file.
2. Commit and push changes.
3. It will be published to the version.
Example: When you change any file in `versioned_docs/version-2.6/`, it will only affect the docs for version `2.6`.
### Deleting an existing version {#deleting-an-existing-version}
You can delete/remove versions as well.
1. Remove the version from `versions.json`.
Example:
```diff
[
"2.0.0",
"1.9.0",
// highlight-next-line
- "1.8.0"
]
```
2. Delete the versioned docs directory. Example: `versioned_docs/version-1.8.0`.
3. Delete the versioned sidebars file. Example: `versioned_sidebars/version-1.8.0-sidebars.json`.
## Configuring versioning behavior {#configuring-versioning-behavior}
The "current" version is the version name for the `./docs` folder. There are different ways to manage versioning, but two very common patterns are:
- You release v1, and start immediately working on v2 (including its docs). In this case, the **current version** is v2, which is in the `./docs` source folder, and can be browsed at `example.com/docs/next`. The **latest version** is v1, which is in the `./versioned_docs/version-1` source folder, and is browsed by most of your users at `example.com/docs`.
- You release v1, and will maintain it for some time before thinking about v2. In this case, the **current version** and **latest version** will both be point to v1, since the v2 docs doesn't even exist yet!
Docusaurus defaults work great for the first use case. We will label the current version as "next" and you can even choose not to publish it.
**For the 2nd use case**: if you release v1 and don't plan to work on v2 anytime soon, instead of versioning v1 and having to maintain the docs in 2 folders (`./docs` + `./versioned_docs/version-1.0.0`), you may consider "pretending" that the current version is a cut version by giving it a path and a label:
```js title="docusaurus.config.js"
export default {
presets: [
'@docusaurus/preset-classic',
docs: {
// highlight-start
lastVersion: 'current',
versions: {
current: {
label: '1.0.0',
path: '1.0.0',
},
},
// highlight-end
},
],
};
```
The docs in `./docs` will be served at `/docs/1.0.0` instead of `/docs/next`, and `1.0.0` will become the default version we link to in the navbar dropdown, and you will only need to maintain a single `./docs` folder.
We offer these plugin options to customize versioning behavior:
- `disableVersioning`: Explicitly disable versioning even with versions. This will make the site only include the current version.
- `includeCurrentVersion`: Include the current version (the `./docs` folder) of your docs.
- **Tip**: turn it off if the current version is a work-in-progress, not ready to be published.
- `lastVersion`: Sets which version "latest version" (the `/docs` route) refers to.
- **Tip**: `lastVersion: 'current'` makes sense if your current version refers to a major version that's constantly patched and released. The actual route base path and label of the latest version are configurable.
- `onlyIncludeVersions`: Defines a subset of versions from `versions.json` to be deployed.
- **Tip**: limit to 2 or 3 versions in dev and deploy previews to improve startup and build time.
- `versions`: A dictionary of version metadata. For each version, you can customize the following:
- `label`: the label displayed in the versions dropdown and banner.
- `path`: the route base path of this version. By default, latest version has `/` and current version has `/next`.
- `banner`: one of `'none'`, `'unreleased'`, and `'unmaintained'`. Determines what's displayed at the top of every doc page. Any version above the latest version would be "unreleased", and any version below would be "unmaintained".
- `badge`: show a badge with the version name at the top of a doc of that version.
- `className`: add a custom `className` to the `<html>` element of doc pages of that version.
See [docs plugin configuration](../../api/plugins/plugin-content-docs.mdx#configuration) for more details.
## Navbar items {#navbar-items}
We offer several navbar items to help you quickly set up navigation without worrying about versioned routes.
- [`doc`](../../api/themes/theme-configuration.mdx#navbar-doc-link): a link to a doc.
- [`docSidebar`](../../api/themes/theme-configuration.mdx#navbar-doc-sidebar): a link to the first item in a sidebar.
- [`docsVersion`](../../api/themes/theme-configuration.mdx#navbar-docs-version): a link to the main doc of the currently viewed version.
- [`docsVersionDropdown`](../../api/themes/theme-configuration.mdx#navbar-docs-version-dropdown): a dropdown containing all the versions available.
These links would all look for an appropriate version to link to, in the following order:
1. **Active version**: the version that the user is currently browsing, if she is on a page provided by this doc plugin. If she's not on a doc page, fall back to...
2. **Preferred version**: the version that the user last viewed. If there's no history, fall back to...
3. **Latest version**: the default version that we navigate to, configured by the `lastVersion` option.
## Recommended practices {#recommended-practices}
### Version your documentation only when needed {#version-your-documentation-only-when-needed}
For example, you are building documentation for your npm package `foo` and you are currently in version 1.0.0. You then release a patch version for a minor bug fix and it's now 1.0.1.
Should you cut a new documentation version 1.0.1? **You probably shouldn't**. 1.0.1 and 1.0.0 docs shouldn't differ according to semver because there are no new features!. Cutting a new version for it will only just create unnecessary duplicated files.
### Keep the number of versions small {#keep-the-number-of-versions-small}
As a good rule of thumb, try to keep the number of your versions below 10. You will **very likely** to have a lot of obsolete versioned documentation that nobody even reads anymore. For example, [Jest](https://jestjs.io/versions) is currently in version `27.4`, and only maintains several latest documentation versions with the lowest being `25.X`. Keep it small 😊
:::tip archive older versions
If you deploy your site on a Jamstack provider (e.g. [Netlify](../../deployment.mdx)), the provider will save each production build as a snapshot under an immutable URL. You can include archived versions that will never be rebuilt as external links to these immutable URLs. The Jest website and the Docusaurus website both use such pattern to keep the number of actively built versions low.
:::
### Use absolute import within the docs {#use-absolute-import-within-the-docs}
Don't use relative paths import within the docs. Because when we cut a version the paths no longer work (the nesting level is different, among other reasons). You can utilize the `@site` alias provided by Docusaurus that points to the `website` directory. Example:
```diff
- import Foo from '../src/components/Foo';
+ import Foo from '@site/src/components/Foo';
```
### Link docs by file paths {#link-docs-by-file-paths}
Refer to other docs by relative file paths with the `.md` extension, so that Docusaurus can rewrite them to actual URL paths during building. Files will be linked to the correct corresponding version.
```md
The [@hello](hello.mdx#paginate) document is great!
See the [Tutorial](../getting-started/tutorial.mdx) for more info.
```
### Global or versioned collocated assets {#global-or-versioned-collocated-assets}
You should decide if assets like images and files are per-version or shared between versions.
If your assets should be versioned, put them in the docs version, and use relative paths:
```md
![img alt](./myImage.png)
[download this file](./file.pdf)
```
If your assets are global, put them in `/static` and use absolute paths:
```md
![img alt](/myImage.png)
[download this file](/file.pdf)
```

View file

@ -0,0 +1,3 @@
<span>Hello {props.name}</span>
This is text some content from `_markdown-partial-example.md`.

View file

@ -0,0 +1,372 @@
---
id: admonitions
description: Handling admonitions/callouts in Docusaurus Markdown
slug: /markdown-features/admonitions
---
# Admonitions
import BrowserWindow from '@site/src/components/BrowserWindow';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Admonition from '@theme/Admonition';
In addition to the basic Markdown syntax, we have a special admonitions syntax by wrapping text with a set of 3 colons, followed by a label denoting its type.
Example:
```md
:::note
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::tip
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::info
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::warning
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::danger
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
```
```mdx-code-block
<BrowserWindow>
:::note
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::tip
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::info
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::warning
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
:::danger
Some **content** with _Markdown_ `syntax`. Check [this `api`](#).
:::
</BrowserWindow>
```
## Usage with Prettier {#usage-with-prettier}
If you use [Prettier](https://prettier.io) to format your Markdown files, Prettier might auto-format your code to invalid admonition syntax. To avoid this problem, add empty lines around the starting and ending directives. This is also why the examples we show here all have empty lines around the content.
{/* prettier-ignore */}
```md
<!-- Prettier doesn't change this -->
:::note
Hello world
:::
<!-- Prettier changes this -->
:::note
Hello world
:::
<!-- to this -->
::: note Hello world:::
```
## Specifying title {#specifying-title}
You may also specify an optional title.
```md
:::note[Your Title **with** some _Markdown_ `syntax`!]
Some **content** with some _Markdown_ `syntax`.
:::
```
```mdx-code-block
<BrowserWindow>
:::note[Your Title **with** some _Markdown_ `syntax`!]
Some **content** with some _Markdown_ `syntax`.
:::
</BrowserWindow>
```
## Nested admonitions {#nested-admonitions}
Admonitions can be nested. Use more colons `:` for each parent admonition level.
```md
:::::info Parent
Parent content
::::danger Child
Child content
:::tip Deep Child
Deep child content
:::
::::
:::::
```
```mdx-code-block
<BrowserWindow>
:::::info Parent
Parent content
::::danger Child
Child content
:::tip Deep Child
Deep child content
:::
::::
:::::
</BrowserWindow>
```
## Admonitions with MDX {#admonitions-with-mdx}
You can use MDX inside admonitions too!
```jsx
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
:::tip[Use tabs in admonitions]
<Tabs>
<TabItem value="apple" label="Apple">This is an apple 🍎</TabItem>
<TabItem value="orange" label="Orange">This is an orange 🍊</TabItem>
<TabItem value="banana" label="Banana">This is a banana 🍌</TabItem>
</Tabs>
:::
```
```mdx-code-block
<BrowserWindow>
:::tip[Use tabs in admonitions]
<Tabs>
<TabItem value="apple" label="Apple">This is an apple 🍎</TabItem>
<TabItem value="orange" label="Orange">This is an orange 🍊</TabItem>
<TabItem value="banana" label="Banana">This is a banana 🍌</TabItem>
</Tabs>
:::
</BrowserWindow>
```
## Usage in JSX {#usage-in-jsx}
Outside of Markdown, you can use the `@theme/Admonition` component to get the same output.
```jsx title="MyReactPage.jsx"
import Admonition from '@theme/Admonition';
export default function MyReactPage() {
return (
<div>
<Admonition type="info">
<p>Some information</p>
</Admonition>
</div>
);
}
```
The types that are accepted are the same as above: `note`, `tip`, `danger`, `info`, `warning`. Optionally, you can specify an icon by passing a JSX element or a string, or a title:
```jsx title="MyReactPage.jsx"
<Admonition type="tip" icon="💡" title="Did you know...">
Use plugins to introduce shorter syntax for the most commonly used JSX
elements in your project.
</Admonition>
```
```mdx-code-block
<BrowserWindow>
<Admonition type="tip" icon="💡" title="Did you know...">
Use plugins to introduce shorter syntax for the most commonly used JSX
elements in your project.
</Admonition>
</BrowserWindow>
```
## Customizing admonitions {#customizing-admonitions}
There are two kinds of customizations possible with admonitions: **parsing** and **rendering**.
### Customizing rendering behavior {#customizing-rendering-behavior}
You can customize how each individual admonition type is rendered through [swizzling](../../swizzling.mdx). You can often achieve your goal through a simple wrapper. For example, in the follow example, we swap out the icon for `info` admonitions only.
```jsx title="src/theme/Admonition.js"
import React from 'react';
import Admonition from '@theme-original/Admonition';
import MyCustomNoteIcon from '@site/static/img/info.svg';
export default function AdmonitionWrapper(props) {
if (props.type !== 'info') {
return <Admonition title="My Custom Admonition Title" {...props} />;
}
return <Admonition icon={<MyCustomNoteIcon />} {...props} />;
}
```
### Customizing parsing behavior {#customizing-parsing-behavior}
Admonitions are implemented with a [Remark plugin](./markdown-features-plugins.mdx). The plugin is designed to be configurable. To customize the Remark plugin for a specific content plugin (docs, blog, pages), pass the options through the `admonitions` key.
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
admonitions: {
keywords: ['note', 'tip', 'info', 'warning', 'danger'],
extendDefaults: true,
},
},
},
],
],
};
```
The plugin accepts the following options:
- `keywords`: An array of keywords that can be used as the type for the admonition.
- `extendDefaults`: Should the provided options (such as `keywords`) be merged into the existing defaults. Defaults to `true`.
The `keyword` will be passed as the `type` prop of the `Admonition` component.
### Custom admonition type components {#custom-admonition-type-components}
By default, the theme doesn't know what do to with custom admonition keywords such as `:::my-custom-admonition`. It is your responsibility to map each admonition keyword to a React component so that the theme knows how to render them.
If you registered a new admonition type `my-custom-admonition` via the following config:
```js title="docusaurus.config.js"
export default {
// ...
presets: [
[
'classic',
{
// ...
docs: {
admonitions: {
keywords: ['my-custom-admonition'],
extendDefaults: true,
},
},
},
],
],
};
```
You can provide the corresponding React component for `:::my-custom-admonition` by creating the following file (unfortunately, since it's not a React component file, it's not swizzlable):
```js title="src/theme/Admonition/Types.js"
import React from 'react';
import DefaultAdmonitionTypes from '@theme-original/Admonition/Types';
function MyCustomAdmonition(props) {
return (
<div style={{border: 'solid red', padding: 10}}>
<h5 style={{color: 'blue', fontSize: 30}}>{props.title}</h5>
<div>{props.children}</div>
</div>
);
}
const AdmonitionTypes = {
...DefaultAdmonitionTypes,
// Add all your custom admonition types here...
// You can also override the default ones if you want
'my-custom-admonition': MyCustomAdmonition,
};
export default AdmonitionTypes;
```
Now you can use your new admonition keyword in a Markdown file, and it will be parsed and rendered with your custom logic:
```md
:::my-custom-admonition[My Title]
It works!
:::
```
<BrowserWindow>
:::my-custom-admonition[My Title]
It works!
:::
</BrowserWindow>

View file

@ -0,0 +1,235 @@
---
id: assets
description: Handling assets in Docusaurus Markdown
slug: /markdown-features/assets
---
# Assets
import BrowserWindow from '@site/src/components/BrowserWindow';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Sometimes you want to link to assets (e.g. docx files, images...) directly from Markdown files, and it is convenient to co-locate the asset next to the Markdown file using it.
Let's imagine the following file structure:
```
# Your doc
/website/docs/myFeature.mdx
# Some assets you want to use
/website/docs/assets/docusaurus-asset-example-banner.png
/website/docs/assets/docusaurus-asset-example.docx
```
## Images {#images}
You can display images in three different ways: Markdown syntax, CJS require, or ES imports syntax.
```mdx-code-block
<Tabs>
<TabItem value="Markdown syntax">
```
Display images using simple Markdown syntax:
```md
![Example banner](./assets/docusaurus-asset-example-banner.png)
```
```mdx-code-block
</TabItem>
<TabItem value="CommonJS require">
```
Display images using inline CommonJS `require` in JSX image tag:
```jsx
<img
src={require('./assets/docusaurus-asset-example-banner.png').default}
alt="Example banner"
/>
```
```mdx-code-block
</TabItem>
<TabItem value="Import statement">
```
Display images using ES `import` syntax and JSX image tag:
```jsx
import myImageUrl from './assets/docusaurus-asset-example-banner.png';
<img src={myImageUrl} alt="Example banner" />;
```
```mdx-code-block
</TabItem>
</Tabs>
```
All of the above result in displaying the image:
<BrowserWindow>
![My image alternative text](../../assets/docusaurus-asset-example-banner.png)
</BrowserWindow>
:::note
If you are using [@docusaurus/plugin-ideal-image](../../api/plugins/plugin-ideal-image.mdx), you need to use the dedicated image component, as documented.
:::
## Files {#files}
In the same way, you can link to existing assets by `require`'ing them and using the returned URL in `video`s, `a` anchor links, etc.
```md
# My Markdown page
<a target="\_blank" href={require('./assets/docusaurus-asset-example.docx').default}> Download this docx </a>
or
[Download this docx using Markdown](./assets/docusaurus-asset-example.docx)
```
<BrowserWindow>
<a
target="_blank"
href={require('../../assets/docusaurus-asset-example.docx').default}>
{'Download this docx'}
</a>
[Download this docx using Markdown](../../assets/docusaurus-asset-example.docx)
</BrowserWindow>
:::info Markdown links are always file paths
If you use the Markdown image or link syntax, all asset paths will be resolved as file paths by Docusaurus and automatically converted to `require()` calls. You don't need to use `require()` in Markdown unless you use the JSX syntax, which you do have to handle yourself.
:::
## Inline SVGs {#inline-svgs}
Docusaurus supports inlining SVGs out of the box.
```jsx
import DocusaurusSvg from './docusaurus.svg';
<DocusaurusSvg />;
```
<BrowserWindow>
import DocusaurusSvg from '@site/static/img/docusaurus.svg';
<DocusaurusSvg />
</BrowserWindow>
This can be useful if you want to alter the part of the SVG image via CSS. For example, you can change one of the SVG colors based on the current theme.
```jsx
import DocusaurusSvg from './docusaurus.svg';
<DocusaurusSvg className="themedDocusaurus" />;
```
```css
[data-theme='light'] .themedDocusaurus [fill='#FFFF50'] {
fill: greenyellow;
}
[data-theme='dark'] .themedDocusaurus [fill='#FFFF50'] {
fill: seagreen;
}
```
<BrowserWindow>
<DocusaurusSvg className="themedDocusaurus" />
</BrowserWindow>
## Themed Images {#themed-images}
Docusaurus supports themed images: the `ThemedImage` component (included in the themes) allows you to switch the image source based on the current theme.
```jsx
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
<ThemedImage
alt="Docusaurus themed image"
// highlight-start
sources={{
light: useBaseUrl('/img/docusaurus_light.svg'),
dark: useBaseUrl('/img/docusaurus_dark.svg'),
}}
// highlight-end
/>;
```
```mdx-code-block
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
<BrowserWindow>
<ThemedImage
alt="Docusaurus themed image"
sources={{
light: useBaseUrl('/img/docusaurus_keytar.svg'),
dark: useBaseUrl('/img/docusaurus_speed.svg'),
}}
/>
</BrowserWindow>
```
### GitHub-style themed images {#github-style-themed-images}
GitHub uses its own [image theming approach](https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/) with path fragments, which you can easily implement yourself.
To toggle the visibility of an image using the path fragment (for GitHub, it's `#gh-dark-mode-only` and `#gh-light-mode-only`), add the following to your custom CSS (you can also use your own suffix if you don't want to be coupled to GitHub):
```css title="src/css/custom.css"
[data-theme='light'] img[src$='#gh-dark-mode-only'],
[data-theme='dark'] img[src$='#gh-light-mode-only'] {
display: none;
}
```
```md
![Docusaurus themed image](/img/docusaurus_keytar.svg#gh-light-mode-only)![Docusaurus themed image](/img/docusaurus_speed.svg#gh-dark-mode-only)
```
<BrowserWindow>
![Docusaurus themed image](/img/docusaurus_keytar.svg#gh-light-mode-only)![Docusaurus themed image](/img/docusaurus_speed.svg#gh-dark-mode-only)
</BrowserWindow>
## Static assets {#static-assets}
If a Markdown link or image has an absolute path, the path will be seen as a file path and will be resolved from the static directories. For example, if you have configured [static directories](../../static-assets.mdx) to be `['public', 'static']`, then for the following image:
```md title="my-doc.md"
![An image from the static](/img/docusaurus.png)
```
Docusaurus will try to look for it in both `static/img/docusaurus.png` and `public/img/docusaurus.png`. The link will then be converted to a `require()` call instead of staying as a URL. This is desirable in two regards:
1. You don't have to worry about the base URL, which Docusaurus will take care of when serving the asset;
2. The image enters Webpack's build pipeline and its name will be appended by a hash, which enables browsers to aggressively cache the image and improves your site's performance.
If you intend to write URLs, you can use the `pathname://` protocol to disable automatic asset linking.
```md
![banner](pathname:///img/docusaurus-asset-example-banner.png)
```
This link will be generated as `<img src="/img/docusaurus-asset-example-banner.png" alt="banner" />`, without any processing or file existence checking.

View file

@ -0,0 +1,852 @@
---
id: code-blocks
description: Handling code blocks in Docusaurus Markdown
slug: /markdown-features/code-blocks
---
# Code blocks
import BrowserWindow from '@site/src/components/BrowserWindow';
import CodeBlock from '@theme/CodeBlock';
Code blocks within documentation are super-powered 💪.
## Code title {#code-title}
You can add a title to the code block by adding a `title` key after the language (leave a space between them).
````md
```jsx title="/src/components/HelloCodeTitle.js"
function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}
```
````
```mdx-code-block
<BrowserWindow>
```
```jsx title="/src/components/HelloCodeTitle.js"
function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}
```
```mdx-code-block
</BrowserWindow>
```
## Syntax highlighting {#syntax-highlighting}
Code blocks are text blocks wrapped around by strings of 3 backticks. You may check out [this reference](https://github.com/mdx-js/specification) for the specifications of MDX.
````md
```js
console.log('Every repo must come with a mascot.');
```
````
Use the matching language meta string for your code block, and Docusaurus will pick up syntax highlighting automatically, powered by [Prism React Renderer](https://github.com/FormidableLabs/prism-react-renderer).
<BrowserWindow>
```js
console.log('Every repo must come with a mascot.');
```
</BrowserWindow>
### Theming {#theming}
By default, the Prism [syntax highlighting theme](https://github.com/FormidableLabs/prism-react-renderer#theming) we use is [Palenight](https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/prism-react-renderer/src/themes/palenight.ts). You can change this to another theme by passing `theme` field in `prism` as `themeConfig` in your docusaurus.config.js.
For example, if you prefer to use the `dracula` highlighting theme:
```js title="docusaurus.config.js"
import {themes as prismThemes} from 'prism-react-renderer';
export default {
themeConfig: {
prism: {
// highlight-next-line
theme: prismThemes.dracula,
},
},
};
```
Because a Prism theme is just a JS object, you can also write your own theme if you are not satisfied with the default. Docusaurus enhances the `github` and `vsDark` themes to provide richer highlight, and you can check our implementations for the [light](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismLight.ts) and [dark](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismDark.ts) code block themes.
### Supported Languages {#supported-languages}
By default, Docusaurus comes with a subset of [commonly used languages](https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/generate-prism-languages/index.ts#L9-L23).
:::warning
Some popular languages like Java, C#, or PHP are not enabled by default.
:::
To add syntax highlighting for any of the other [Prism-supported languages](https://prismjs.com/#supported-languages), define it in an array of additional languages.
:::note
Each additional language has to be a valid Prism component name. For example, Prism would map the _language_ `cs` to `csharp`, but only `prism-csharp.js` exists as a _component_, so you need to use `additionalLanguages: ['csharp']`. You can look into `node_modules/prismjs/components` to find all components (languages) available.
:::
For example, if you want to add highlighting for the PowerShell language:
```js title="docusaurus.config.js"
export default {
// ...
themeConfig: {
prism: {
// highlight-next-line
additionalLanguages: ['powershell'],
},
// ...
},
};
```
After adding `additionalLanguages`, restart Docusaurus.
If you want to add highlighting for languages not yet supported by Prism, you can swizzle `prism-include-languages`:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic prism-include-languages
```
It will produce `prism-include-languages.js` in your `src/theme` folder. You can add highlighting support for custom languages by editing `prism-include-languages.js`:
```js title="src/theme/prism-include-languages.js"
const prismIncludeLanguages = (Prism) => {
// ...
additionalLanguages.forEach((lang) => {
require(`prismjs/components/prism-${lang}`);
});
// highlight-next-line
require('/path/to/your/prism-language-definition');
// ...
};
```
You can refer to [Prism's official language definitions](https://github.com/PrismJS/prism/tree/master/components) when you are writing your own language definitions.
When adding a custom language definition, you do not need to add the language to the `additionalLanguages` config array, since Docusaurus only looks up the `additionalLanguages` strings in languages that Prism provides. Adding the language import in `prism-include-languages.js` is sufficient.
## Line highlighting {#line-highlighting}
### Highlighting with comments {#highlighting-with-comments}
You can use comments with `highlight-next-line`, `highlight-start`, and `highlight-end` to select which lines are highlighted.
````md
```js
function HighlightSomeText(highlight) {
if (highlight) {
// highlight-next-line
return 'This text is highlighted!';
}
return 'Nothing highlighted';
}
function HighlightMoreText(highlight) {
// highlight-start
if (highlight) {
return 'This range is highlighted!';
}
// highlight-end
return 'Nothing highlighted';
}
```
````
```mdx-code-block
<BrowserWindow>
```
```js
function HighlightSomeText(highlight) {
if (highlight) {
// highlight-next-line
return 'This text is highlighted!';
}
return 'Nothing highlighted';
}
function HighlightMoreText(highlight) {
// highlight-start
if (highlight) {
return 'This range is highlighted!';
}
// highlight-end
return 'Nothing highlighted';
}
```
```mdx-code-block
</BrowserWindow>
```
Supported commenting syntax:
| Style | Syntax |
| ---------- | ------------------------ |
| C-style | `/* ... */` and `// ...` |
| JSX-style | `{/* ... */}` |
| Bash-style | `# ...` |
| HTML-style | `<!-- ... -->` |
We will do our best to infer which set of comment styles to use based on the language, and default to allowing _all_ comment styles. If there's a comment style that is not currently supported, we are open to adding them! Pull requests welcome. Note that different comment styles have no semantic difference, only their content does.
You can set your own background color for highlighted code line in your `src/css/custom.css` which will better fit to your selected syntax highlighting theme. The color given below works for the default highlighting theme (Palenight), so if you are using another theme, you will have to tweak the color accordingly.
```css title="/src/css/custom.css"
:root {
--docusaurus-highlighted-code-line-bg: rgb(72, 77, 91);
}
/* If you have a different syntax highlighting theme for dark mode. */
[data-theme='dark'] {
/* Color which works with dark mode syntax highlighting theme */
--docusaurus-highlighted-code-line-bg: rgb(100, 100, 100);
}
```
If you also need to style the highlighted code line in some other way, you can target on `theme-code-block-highlighted-line` CSS class.
### Highlighting with metadata string {#highlighting-with-metadata-string}
You can also specify highlighted line ranges within the language meta string (leave a space after the language). To highlight multiple lines, separate the line numbers by commas or use the range syntax to select a chunk of lines. This feature uses the `parse-number-range` library and you can find [more syntax](https://www.npmjs.com/package/parse-numeric-range) on their project details.
````md
```jsx {1,4-6,11}
import React from 'react';
function MyComponent(props) {
if (props.isBar) {
return <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
````
```mdx-code-block
<BrowserWindow>
```
```jsx {1,4-6,11}
import React from 'react';
function MyComponent(props) {
if (props.isBar) {
return <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
```mdx-code-block
</BrowserWindow>
```
:::tip prefer comments
Prefer highlighting with comments where you can. By inlining highlight in the code, you don't have to manually count the lines if your code block becomes long. If you add/remove lines, you also don't have to offset your line ranges.
````diff
- ```jsx {3}
+ ```jsx {4}
function HighlightSomeText(highlight) {
if (highlight) {
+ console.log('Highlighted text found');
return 'This text is highlighted!';
}
return 'Nothing highlighted';
}
```
````
Below, we will introduce how the magic comment system can be extended to define custom directives and their functionalities. The magic comments would only be parsed if a highlight metastring is not present.
:::
### Custom magic comments {#custom-magic-comments}
`// highlight-next-line` and `// highlight-start` etc. are called "magic comments", because they will be parsed and removed, and their purposes are to add metadata to the next line, or the section that the pair of start- and end-comments enclose.
You can declare custom magic comments through theme config. For example, you can register another magic comment that adds a `code-block-error-line` class name:
```mdx-code-block
<Tabs>
<TabItem value="docusaurus.config.js">
```
```js
export default {
themeConfig: {
prism: {
magicComments: [
// Remember to extend the default highlight class name as well!
{
className: 'theme-code-block-highlighted-line',
line: 'highlight-next-line',
block: {start: 'highlight-start', end: 'highlight-end'},
},
// highlight-start
{
className: 'code-block-error-line',
line: 'This will error',
},
// highlight-end
],
},
},
};
```
```mdx-code-block
</TabItem>
<TabItem value="src/css/custom.css">
```
```css
.code-block-error-line {
background-color: #ff000020;
display: block;
margin: 0 calc(-1 * var(--ifm-pre-padding));
padding: 0 var(--ifm-pre-padding);
border-left: 3px solid #ff000080;
}
```
```mdx-code-block
</TabItem>
<TabItem value="myDoc.md">
```
````md
In JavaScript, trying to access properties on `null` will error.
```js
const name = null;
// This will error
console.log(name.toUpperCase());
// Uncaught TypeError: Cannot read properties of null (reading 'toUpperCase')
```
````
```mdx-code-block
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
```
In JavaScript, trying to access properties on `null` will error.
```js
const name = null;
// This will error
console.log(name.toUpperCase());
// Uncaught TypeError: Cannot read properties of null (reading 'toUpperCase')
```
```mdx-code-block
</BrowserWindow>
```
If you use number ranges in metastring (the `{1,3-4}` syntax), Docusaurus will apply the **first `magicComments` entry**'s class name. This, by default, is `theme-code-block-highlighted-line`, but if you change the `magicComments` config and use a different entry as the first one, the meaning of the metastring range will change as well.
You can disable the default line highlighting comments with `magicComments: []`. If there's no magic comment config, but Docusaurus encounters a code block containing a metastring range, it will error because there will be no class name to apply—the highlighting class name, after all, is just a magic comment entry.
Every magic comment entry will contain three keys: `className` (required), `line`, which applies to the directly next line, or `block` (containing `start` and `end`), which applies to the entire block enclosed by the two comments.
Using CSS to target the class can already do a lot, but you can unlock the full potential of this feature through [swizzling](../../swizzling.mdx).
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic CodeBlock/Line
```
The `Line` component will receive the list of class names, based on which you can conditionally render different markup.
## Line numbering {#line-numbering}
You can enable line numbering for your code block by using `showLineNumbers` key within the language meta string (don't forget to add space directly before the key).
````md
```jsx {1,4-6,11} showLineNumbers
import React from 'react';
function MyComponent(props) {
if (props.isBar) {
return <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
````
```mdx-code-block
<BrowserWindow>
```
```jsx {1,4-6,11} showLineNumbers
import React from 'react';
function MyComponent(props) {
if (props.isBar) {
return <div>Bar</div>;
}
return <div>Foo</div>;
}
export default MyComponent;
```
```mdx-code-block
</BrowserWindow>
```
## Interactive code editor {#interactive-code-editor}
(Powered by [React Live](https://github.com/FormidableLabs/react-live))
You can create an interactive coding editor with the `@docusaurus/theme-live-codeblock` plugin. First, add the plugin to your package.
```bash npm2yarn
npm install --save @docusaurus/theme-live-codeblock
```
You will also need to add the plugin to your `docusaurus.config.js`.
```js {3}
export default {
// ...
themes: ['@docusaurus/theme-live-codeblock'],
// ...
};
```
To use the plugin, create a code block with `live` attached to the language meta string.
````md
```jsx live
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
const timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
```
````
The code block will be rendered as an interactive editor. Changes to the code will reflect on the result panel live.
```mdx-code-block
<BrowserWindow>
```
```jsx live
function Clock(props) {
const [date, setDate] = useState(new Date());
useEffect(() => {
const timerID = setInterval(() => tick(), 1000);
return function cleanup() {
clearInterval(timerID);
};
});
function tick() {
setDate(new Date());
}
return (
<div>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
```
```mdx-code-block
</BrowserWindow>
```
### Imports {#imports}
:::warning react-live and imports
It is not possible to import components directly from the react-live code editor, you have to define available imports upfront.
:::
By default, all React imports are available. If you need more imports available, swizzle the react-live scope:
```bash npm2yarn
npm run swizzle @docusaurus/theme-live-codeblock ReactLiveScope -- --eject
```
```jsx title="src/theme/ReactLiveScope/index.js"
import React from 'react';
// highlight-start
const ButtonExample = (props) => (
<button
{...props}
style={{
backgroundColor: 'white',
color: 'black',
border: 'solid red',
borderRadius: 20,
padding: 10,
cursor: 'pointer',
...props.style,
}}
/>
);
// highlight-end
// Add react-live imports you need here
const ReactLiveScope = {
React,
...React,
// highlight-next-line
ButtonExample,
};
export default ReactLiveScope;
```
The `ButtonExample` component is now available to use:
```mdx-code-block
<BrowserWindow>
```
```jsx live
function MyPlayground(props) {
return (
<div>
<ButtonExample onClick={() => alert('hey!')}>Click me</ButtonExample>
</div>
);
}
```
```mdx-code-block
</BrowserWindow>
```
### Imperative Rendering (noInline)
The `noInline` option should be used to avoid errors when your code spans multiple components or variables.
````md
```jsx live noInline
const project = 'Docusaurus';
const Greeting = () => <p>Hello {project}!</p>;
render(<Greeting />);
```
````
Unlike an ordinary interactive code block, when using `noInline` React Live won't wrap your code in an inline function to render it.
You will need to explicitly call `render()` at the end of your code to display the output.
````mdx-code-block
<BrowserWindow>
```jsx live noInline
const project = "Docusaurus";
const Greeting = () => (
<p>Hello {project}!</p>
);
render(
<Greeting />
);
```
</BrowserWindow>
````
## Using JSX markup in code blocks {#using-jsx-markup}
Code block in Markdown always preserves its content as plain text, meaning you can't do something like:
```ts
type EditUrlFunction = (params: {
// This doesn't turn into a link (for good reason!)
version: <a href="/docs/versioning">Version</a>;
versionDocsDirPath: string;
docPath: string;
permalink: string;
locale: string;
}) => string | undefined;
```
If you want to embed HTML markup such as anchor links or bold type, you can use the `<pre>` tag, `<code>` tag, or `<CodeBlock>` component.
```jsx
<pre>
<b>Input: </b>1 2 3 4{'\n'}
<b>Output: </b>"366300745"{'\n'}
</pre>
```
<BrowserWindow>
<pre>
<b>{'Input: '}</b>
{'1 2 3 4\n'}
<b>{'Output: '}</b>
{'"366300745"\n'}
</pre>
</BrowserWindow>
:::warning MDX is whitespace insensitive
MDX is in line with JSX behavior: line break characters, even when inside `<pre>`, are turned into spaces. You have to explicitly write the new line character for it to be printed out.
:::
:::warning
Syntax highlighting only works on plain strings. Docusaurus will not attempt to parse code block content containing JSX children.
:::
## Multi-language support code blocks {#multi-language-support-code-blocks}
With MDX, you can easily create interactive components within your documentation, for example, to display code in multiple programming languages and switch between them using a tabs component.
Instead of implementing a dedicated component for multi-language support code blocks, we've implemented a general-purpose [`<Tabs>`](./markdown-features-tabs.mdx) component in the classic theme so that you can use it for other non-code scenarios as well.
The following example is how you can have multi-language code tabs in your docs. Note that the empty lines above and below each language block are **intentional**. This is a [current limitation of MDX](./markdown-features-react.mdx#markdown-and-jsx-interoperability): you have to leave empty lines around Markdown syntax for the MDX parser to know that it's Markdown syntax and not JSX.
````jsx
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="js" label="JavaScript">
```js
function helloWorld() {
console.log('Hello, world!');
}
```
</TabItem>
<TabItem value="py" label="Python">
```py
def hello_world():
print("Hello, world!")
```
</TabItem>
<TabItem value="java" label="Java">
```java
class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello, World");
}
}
```
</TabItem>
</Tabs>
````
And you will get the following:
```mdx-code-block
<BrowserWindow>
<Tabs>
<TabItem value="js" label="JavaScript">
```
```js
function helloWorld() {
console.log('Hello, world!');
}
```
```mdx-code-block
</TabItem>
<TabItem value="py" label="Python">
```
```py
def hello_world():
print("Hello, world!")
```
```mdx-code-block
</TabItem>
<TabItem value="java" label="Java">
```
```java
class HelloWorld {
public static void main(String args[]) {
System.out.println("Hello, World");
}
}
```
```mdx-code-block
</TabItem>
</Tabs>
</BrowserWindow>
```
If you have multiple of these multi-language code tabs, and you want to sync the selection across the tab instances, refer to the [Syncing tab choices section](markdown-features-tabs.mdx#syncing-tab-choices).
### Docusaurus npm2yarn remark plugin {#npm2yarn-remark-plugin}
Displaying CLI commands in both npm and Yarn is a very common need, for example:
```bash npm2yarn
npm install @docusaurus/remark-plugin-npm2yarn
```
Docusaurus provides such a utility out of the box, freeing you from using the `Tabs` component every time. To enable this feature, first install the `@docusaurus/remark-plugin-npm2yarn` package as above, and then in `docusaurus.config.js`, for the plugins where you need this feature (doc, blog, pages, etc.), register it in the `remarkPlugins` option. (See [Docs configuration](../../api/plugins/plugin-content-docs.mdx#ex-config) for more details on configuration format)
```js title="docusaurus.config.js"
export default {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-start
remarkPlugins: [
[require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
],
// highlight-end
},
pages: {
// highlight-next-line
remarkPlugins: [require('@docusaurus/remark-plugin-npm2yarn')],
},
blog: {
// highlight-start
remarkPlugins: [
[
require('@docusaurus/remark-plugin-npm2yarn'),
{converters: ['pnpm']},
],
],
// highlight-end
// ...
},
},
],
],
};
```
And then use it by adding the `npm2yarn` key to the code block:
````md
```bash npm2yarn
npm install @docusaurus/remark-plugin-npm2yarn
```
````
#### Configuration {#npm2yarn-remark-plugin-configuration}
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `sync` | `boolean` | `false` | Whether to sync the selected converter across all code blocks. |
| `converters` | `array` | `'yarn'`, `'pnpm'` | The list of converters to use. The order of the converters is important, as the first converter will be used as the default choice. |
## Usage in JSX {#usage-in-jsx}
Outside of Markdown, you can use the `@theme/CodeBlock` component to get the same output.
```jsx
import CodeBlock from '@theme/CodeBlock';
export default function MyReactPage() {
return (
<div>
{/* highlight-start */}
<CodeBlock
language="jsx"
title="/src/components/HelloCodeTitle.js"
showLineNumbers>
{`function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}`}
</CodeBlock>
{/* highlight-end */}
</div>
);
}
```
```mdx-code-block
<BrowserWindow>
<CodeBlock
language="jsx"
title="/src/components/HelloCodeTitle.js"
showLineNumbers>
{`function HelloCodeTitle(props) {
return <h1>Hello, {props.name}</h1>;
}`}
</CodeBlock>
</BrowserWindow>
```
The props accepted are `language`, `title` and `showLineNumbers`, in the same way as you write Markdown code blocks.
Although discouraged, you can also pass in a `metastring` prop like `metastring='{1-2} title="/src/components/HelloCodeTitle.js" showLineNumbers'`, which is how Markdown code blocks are handled under the hood. However, we recommend you [use comments for highlighting lines](#highlighting-with-comments).
As [previously stated](#using-jsx-markup), syntax highlighting is only applied when the children is a simple string.

View file

@ -0,0 +1,101 @@
---
id: diagrams
title: Diagrams
description: Writing diagrams with Mermaid
slug: /markdown-features/diagrams
---
# Diagrams
Diagrams can be rendered using [Mermaid](https://mermaid-js.github.io/mermaid/) in a code block.
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/theme-mermaid
```
Enable Mermaid functionality by adding plugin `@docusaurus/theme-mermaid` and setting `markdown.mermaid` to `true` in your `docusaurus.config.js`.
```js title="docusaurus.config.js"
export default {
markdown: {
mermaid: true,
},
themes: ['@docusaurus/theme-mermaid'],
};
```
## Usage {#usage}
Add a code block with language `mermaid`:
````md title="Example Mermaid diagram"
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
````
```mermaid
graph TD;
A-->B;
A-->C;
B-->D;
C-->D;
```
See the [Mermaid syntax documentation](https://mermaid-js.github.io/mermaid/#/./n00b-syntaxReference) for more information on the Mermaid syntax.
## Theming {#theming}
The diagram dark and light themes can be changed by setting `mermaid.theme` values in the `themeConfig` in your `docusaurus.config.js`. You can set themes for both light and dark mode.
```js title="docusaurus.config.js"
export default {
themeConfig: {
mermaid: {
theme: {light: 'neutral', dark: 'forest'},
},
},
};
```
See the [Mermaid theme documentation](https://mermaid-js.github.io/mermaid/#/theming) for more information on theming Mermaid diagrams.
## Mermaid Config {#configuration}
Options in `mermaid.options` will be passed directly to `mermaid.initialize`:
```js title="docusaurus.config.js"
export default {
themeConfig: {
mermaid: {
options: {
maxTextSize: 50,
},
},
},
};
```
See the [Mermaid config documentation](https://mermaid-js.github.io/mermaid/#/./Setup?id=configuration) and the [Mermaid config types](https://github.com/mermaid-js/mermaid/blob/master/packages/mermaid/src/config.type.ts) for the available config options.
## Dynamic Mermaid Component {#component}
To generate dynamic diagrams, you can use the `Mermaid` component:
```mdx title="Example of dynamic Mermaid component"
import Mermaid from '@theme/Mermaid';
<Mermaid
value={`graph TD;
A-->B;
A-->C;
B-->D;
C-->D;`}
/>
```

View file

@ -0,0 +1,82 @@
---
id: head-metadata
description: Declaring page-specific head metadata through MDX
slug: /markdown-features/head-metadata
---
# Head metadata
## Customizing head metadata {#customizing-head-metadata}
Docusaurus automatically sets useful page metadata in `<html>`, `<head>` and `<body>` for you. It is possible to add extra metadata (or override existing ones) with the `<head>` tag in Markdown files:
```md title="markdown-features-head-metadata.mdx"
---
id: head-metadata
title: Head Metadata
---
<!-- highlight-start -->
<head>
<html className="some-extra-html-class" />
<body className="other-extra-body-class" />
<title>Head Metadata customized title!</title>
<meta charSet="utf-8" />
<meta name="twitter:card" content="summary" />
<link rel="canonical" href="https://docusaurus.io/docs/markdown-features/head-metadata" />
</head>
<!-- highlight-end -->
# Head Metadata
My text
```
```mdx-code-block
<head>
<html className="some-extra-html-class" />
<body className="other-extra-body-class" />
<title>Head Metadata customized title!</title>
<meta charSet="utf-8" />
<meta name="twitter:card" content="summary" />
<link rel="canonical" href="https://docusaurus.io/docs/markdown-features/head-metadata" />
</head>
```
This `<head>` declaration has been added to the current Markdown doc as a demo. Open your browser DevTools and check how this page's metadata has been affected.
:::note
This feature is built on top of the Docusaurus [`<Head>`](./../../docusaurus-core.mdx#head) component. Refer to [react-helmet](https://github.com/nfl/react-helmet) for exhaustive documentation.
:::
:::tip You don't need this for regular SEO
Content plugins (e.g. docs and blog) provide front matter options like `description`, `keywords`, and `image`, which will be automatically applied to both `description` and `og:description`, while you would have to manually declare two metadata tags when using the `<head>` tag.
:::
## Markdown page description {#markdown-page-description}
The Markdown pages' description metadata may be used in more places than the head metadata. For example, the docs plugin's [generated category index](../docs/sidebar/items.mdx#generated-index-page) uses the description metadata for the doc cards.
By default, the description is the first content-ful line, with some efforts to convert it to plain text. For example, the following file...
```md
# Title
Main content... May contain some [links](./file.mdx) or **emphasis**.
```
...will have the default description "Main content... May contain some links or emphasis". However, **it's not designed to be fully functional**. Where it fails to produce reasonable descriptions, you can explicitly provide one through front matter:
```md
---
description: This description will override the default.
---
# Title
Main content... May contain some [links](./file.mdx) or **emphasis**.
```

View file

@ -0,0 +1,237 @@
---
id: introduction
description: Docusaurus uses MDX. Find out more about Docusaurus-specific features when writing Markdown.
slug: /markdown-features
---
# Markdown Features
import BrowserWindow from '@site/src/components/BrowserWindow';
Docusaurus uses **[Markdown](https://commonmark.org/)** as its main content authoring format.
:::tip Learn Markdown
You can [learn Markdown in 10 minutes](https://commonmark.org/help/).
:::
Docusaurus uses modern tooling to help you create **interactive documentation**.
The **[MDX](https://mdxjs.com/)** compiler transforms **Markdown files to React components**, and allows you to use JSX in your Markdown content. This enables you to easily interleave React components within your content, and create delightful learning experiences.
:::tip Use the MDX Playground
The **[MDX playground](https://mdxjs.com/playground/)** is your new best friend!
It is a very helpful debugging tool that shows how the MDX compiler transforms Markdown to React.
**Options**: select the right format (MDX or CommonMark) and the following plugins Docusaurus uses: `remark-gfm`, `remark-directive`, `rehype-raw`.
:::
## MDX vs. CommonMark {#mdx-vs-commonmark}
Docusaurus compiles both `.md` and `.mdx` files to React components using the MDX compiler, but **the syntax can be interpreted differently** depending on your settings.
The MDX compiler supports [2 formats](https://mdxjs.com/packages/mdx/#optionsformat):
- The [MDX format](https://mdxjs.com/docs/what-is-mdx/): a powerful parser allowing the usage of JSX
- The [CommonMark format](https://commonmark.org/): a standard-compliant Markdown parser that does not allow the usage of JSX
By default, **Docusaurus v3 uses the MDX format for all files** (including `.md` files) for historical reasons.
It is possible to **opt-in for CommonMark** using the [`siteConfig.markdown.format`](../../api/docusaurus.config.js.mdx#markdown) setting or the `format: md` front matter.
:::tip how to use CommonMark
If you plan to use CommonMark, we recommend the [`siteConfig.markdown.format: 'detect'`](../../api/docusaurus.config.js.mdx#markdown) setting. The appropriate format will be selected automatically, based on file extensions:
- `.md` files will use the CommonMark format
- `.mdx` files will use the MDX format
:::
:::danger Experimental CommonMark support
The CommonMark support is **experimental** and currently has a few [limitations](https://github.com/facebook/docusaurus/issues/9092).
:::
## Standard features {#standard-features}
Markdown is a syntax that enables you to write formatted content in a readable syntax.
```md
### My Doc Section
Hello world message with some **bold** text, some _italic_ text, and a [link](/)
![img alt](/img/docusaurus.png)
```
```mdx-code-block
<BrowserWindow>
<h3>My Doc Section</h3>
Hello world message with some **bold** text, some _italic_ text and a [link](/)
![img alt](/img/docusaurus.png)
</BrowserWindow>
```
<details>
<summary>Markdown is declarative</summary>
Some may assume a 1-1 correlation between Markdown and HTML, e.g., `![Preview](/img/docusaurus.png)` will always become `<img src="/img/docusaurus.png" alt="Preview" />`, as-is. However, _that is not the case_.
The Markdown syntax `![message](url)` only declaratively tells Docusaurus that an image needs to be inserted here, but we may do other things like transforming a [file path to URL path](./markdown-features-assets.mdx#images), so the generated markup may differ from the output of other Markdown renderers, or a naïve hand-transcription to the equivalent JSX/HTML code.
In general, you should only assume the _semantics_ of the markup (` ``` ` fences become [code blocks](./markdown-features-code-blocks.mdx); `>` becomes [quotes](#quotes), etc.), but not the actual compiled output.
</details>
## Front matter {#front-matter}
Front matter is used to add metadata to your Markdown file. All content plugins have their own front matter schema, and use the front matter to enrich the default metadata inferred from the content or other configuration.
Front matter is provided at the very top of the file, enclosed by three dashes `---`. The content is parsed as [YAML](https://yaml.org/spec/1.2.2/).
```md
---
title: My Doc Title
more_data:
- Can be provided
- as: objects
or: arrays
---
```
:::info
The API documentation of each official plugin lists the supported attributes:
- [Docs front matter](../../api/plugins/plugin-content-docs.mdx#markdown-front-matter)
- [Blog front matter](../../api/plugins/plugin-content-blog.mdx#markdown-front-matter)
- [Pages front matter](../../api/plugins/plugin-content-pages.mdx#markdown-front-matter)
:::
:::tip enhance your front matter
Use the [Markdown config `parseFrontMatter` function](../../api/docusaurus.config.js.mdx#markdown) to provide your own front matter parser, or to enhance the default parser.
It is possible to reuse the default parser to wrap it with your own custom proprietary logic. This makes it possible to implement convenient front matter transformations, shortcuts, or to integrate with external systems using front matter that Docusaurus plugins do not support.
```js title="docusaurus.config.js"
export default {
markdown: {
// highlight-start
parseFrontMatter: async (params) => {
// Reuse the default parser
const result = await params.defaultParseFrontMatter(params);
// Process front matter description placeholders
result.frontMatter.description =
result.frontMatter.description?.replaceAll('{{MY_VAR}}', 'MY_VALUE');
// Create your own front matter shortcut
if (result.frontMatter.i_do_not_want_docs_pagination) {
result.frontMatter.pagination_prev = null;
result.frontMatter.pagination_next = null;
}
// Rename an unsupported front matter coming from another system
if (result.frontMatter.cms_seo_summary) {
result.frontMatter.description = result.frontMatter.cms_seo_summary;
delete result.frontMatter.cms_seo_summary;
}
return result;
},
// highlight-end
},
};
```
:::
## Quotes {#quotes}
Markdown quotes are beautifully styled:
```md
> Easy to maintain open source documentation websites.
>
> — Docusaurus
```
<BrowserWindow>
> Easy to maintain open source documentation websites.
>
> — Docusaurus
</BrowserWindow>
## Details {#details}
Markdown can embed HTML elements, and [`details`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/details) HTML elements are beautifully styled:
{/* prettier-ignore */}
````md
### Details element example
<details>
<summary>Toggle me!</summary>
This is the detailed content
```js
console.log("Markdown features including the code block are available");
```
You can use Markdown here including **bold** and _italic_ text, and [inline link](https://docusaurus.io)
<details>
<summary>Nested toggle! Some surprise inside...</summary>
😲😲😲😲😲
</details>
</details>
````
````mdx-code-block
<BrowserWindow>
<h3>Details element example</h3>
<details>
<summary>Toggle me!</summary>
This is the detailed content
```js
console.log("Markdown features including the code block are available");
```
You can use Markdown here including **bold** and _italic_ text, and [inline link](https://docusaurus.io)
<details>
<summary>Nested toggle! Some surprise inside...</summary>
😲😲😲😲😲
</details>
</details>
</BrowserWindow>
````
:::info
- You should not break lines between the starting or ending tag of the `<summary>` element and its content or an extra `<p>` element is inserted.
- Blank lines between the `<summary>` element and the first paragraph of the detailed content are optional in Docusaurus but strongly recommended to ensure MDX portability with other site generators.
- Blank lines between the nested `<details>` element and the paragraphs just around it are optional.
:::

View file

@ -0,0 +1,52 @@
---
id: links
description: Links to other pages in Markdown
slug: /markdown-features/links
---
# Markdown links
There are two ways of adding a link to another page: through a **URL path** and a **file path**.
```md
- [URL path to another document](./installation)
- [file path to another document](./installation.mdx)
```
URL paths are unprocessed by Docusaurus, and you can see them as directly rendering to `<a href="./installation">`, i.e. it will be resolved according to the page's URL location, rather than its file-system location.
If you want to reference another Markdown file **included by the same plugin**, you could use the relative path of the document you want to link to. Docusaurus' Markdown loader will convert the file path to the target file's URL path (and hence remove the `.md` extension).
For example, if you are in `docs/folder/doc1.md` and you want to reference `docs/folder/doc2.md`, `docs/folder/subfolder/doc3.md` and `docs/otherFolder/doc4.md`:
```md title="docs/folder/doc1.md"
I am referencing a [document](doc2.mdx).
Reference to another [document in a subfolder](subfolder/doc3.mdx).
[Relative document](../otherFolder/doc4.mdx) referencing works as well.
```
Relative file paths are resolved against the current file's directory. Absolute file paths, on the other hand, are resolved relative to the **content root**, usually `docs/`, `blog/`, or [localized ones](../../i18n/i18n-tutorial.mdx) like `i18n/zh-Hans/plugin-content-docs/current`.
Absolute file paths can also be relative to the site directory. However, beware that links that begin with `/docs/` or `/blog/` are **not portable** as you would need to manually update them if you create new doc versions or localize them.
```md
You can write [links](/otherFolder/doc4.mdx) relative to the content root (`/docs/`).
You can also write [links](/docs/otherFolder/doc4.mdx) relative to the site directory, but it's not recommended.
```
Using relative _file_ paths (with `.md` extensions) instead of relative _URL_ links provides the following benefits:
- Links will keep working on the GitHub interface and many Markdown editors
- You can customize the files' slugs without having to update all the links
- Moving files around the folders can be tracked by your editor, and some editors may automatically update file links
- A [versioned doc](../docs/versioning.mdx) will link to another doc of the exact same version
- Relative URL links are very likely to break if you update the [`trailingSlash` config](../../api/docusaurus.config.js.mdx#trailingSlash)
:::warning
Markdown file references only work when the source and target files are processed by the same plugin instance. This is a technical limitation of our Markdown processing architecture and will be fixed in the future. If you are linking files between plugins (e.g. linking to a doc page from a blog post), you have to use URL links.
:::

View file

@ -0,0 +1,202 @@
---
id: math-equations
description: Writing LaTeX Math Equations
slug: /markdown-features/math-equations
---
# Math Equations
import BrowserWindow from '@site/src/components/BrowserWindow';
Mathematical equations can be rendered using [KaTeX](https://katex.org).
## Usage {#usage}
Please read [KaTeX](https://katex.org) documentation for more details.
### Inline {#inline}
Write inline math equations by wrapping LaTeX equations between `$`:
```latex
Let $f\colon[a,b]\to\R$ be Riemann integrable. Let $F\colon[a,b]\to\R$ be
$F(x)=\int_{a}^{x} f(t)\,dt$. Then $F$ is continuous, and at all $x$ such that
$f$ is continuous at $x$, $F$ is differentiable at $x$ with $F'(x)=f(x)$.
```
<BrowserWindow>
Let $f\colon[a,b] \to \R$ be Riemann integrable. Let $F\colon[a,b]\to\R$ be $F(x)=
\int_{a}^{x} f(t)\,dt$. Then $F$ is continuous, and at all $x$ such that $f$ is continuous at $x$, $F$ is differentiable at $x$ with $F'(x)=f(x)$.
</BrowserWindow>
### Blocks {#blocks}
For equation block or display mode, use line breaks and `$$`:
```latex
$$
I = \int_0^{2\pi} \sin(x)\,dx
$$
```
<BrowserWindow>
$$
I = \int_0^{2\pi} \sin(x)\,dx
$$
</BrowserWindow>
## Enabling math equations {#configuration}
Enable KaTeX:
1. Install the `remark-math` and `rehype-katex` plugins:
```bash npm2yarn
npm install --save remark-math@6 rehype-katex@7
```
:::warning
Make sure to use `remark-math 6` and `rehype-katex 7` for Docusaurus v3 (using MDX v3). We can't guarantee other versions will work.
:::
2. These 2 plugins are [**only available as ES Modules**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). We recommended to use an [**ES Modules**](https://flaviocopes.com/es-modules/) config file:
```js title="ES module docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
// highlight-start
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
// highlight-end
},
},
],
],
};
```
<details>
<summary>
Using a
[**CommonJS**](https://nodejs.org/api/modules.html#modules-commonjs-modules)
config file?
</summary>
If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:
```js title="CommonJS module docusaurus.config.js"
// highlight-start
module.exports = async function createConfigAsync() {
// highlight-end
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default],
// highlight-end
},
},
],
],
};
};
```
</details>
3. Include the KaTeX CSS in your config under `stylesheets`:
```js
export default {
//...
stylesheets: [
{
href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
type: 'text/css',
integrity:
'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
crossorigin: 'anonymous',
},
],
};
```
<details>
<summary>See a config file example</summary>
```js title="docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
export default {
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
// highlight-end
},
},
],
],
// highlight-start
stylesheets: [
{
href: 'https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css',
type: 'text/css',
integrity:
'sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM',
crossorigin: 'anonymous',
},
],
// highlight-end
};
```
</details>
## Self-hosting KaTeX assets {#self-hosting-katex-assets}
Loading stylesheets, fonts, and JavaScript libraries from CDN sources is a good practice for popular libraries and assets, since it reduces the amount of assets you have to host. In case you prefer to self-host the `katex.min.css` (along with required KaTeX fonts), you can download the latest version from [KaTeX GitHub releases](https://github.com/KaTeX/KaTeX/releases), extract and copy `katex.min.css` and `fonts` directory (only `.woff2` font types should be enough) to your site's `static` directory, and in `docusaurus.config.js`, replace the stylesheet's `href` from the CDN URL to your local path (say, `/katex/katex.min.css`).
```js title="docusaurus.config.js"
export default {
stylesheets: [
{
href: '/katex/katex.min.css',
type: 'text/css',
},
],
};
```

View file

@ -0,0 +1,247 @@
---
id: plugins
description: Using MDX plugins to expand Docusaurus Markdown functionalities
slug: /markdown-features/plugins
---
# MDX Plugins
Sometimes, you may want to extend or tweak your Markdown syntax. For example:
- How do I embed youtube videos using the image syntax (`![](https://youtu.be/yKNxeF4KMsY)`)?
- How do I style links that are on their own lines differently, e.g., as a social card?
- How do I make every page start with a copyright notice?
And the answer is: create an MDX plugin! MDX has a built-in [plugin system](https://mdxjs.com/advanced/plugins/) that can be used to customize how the Markdown files will be parsed and transformed to JSX. There are three typical use-cases of MDX plugins:
- Using existing [remark plugins](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins) or [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins);
- Creating remark/rehype plugins to transform the elements generated by existing MDX syntax;
- Creating remark/rehype plugins to introduce new syntaxes to MDX.
If you play with the [MDX playground](https://mdxjs.com/playground/), you would notice that the MDX transpilation has two intermediate steps: Markdown AST (MDAST), and Hypertext AST (HAST), before arriving at the final JSX output. MDX plugins also come in two forms:
- **[Remark](https://github.com/remarkjs/remark/)**: processes the Markdown AST.
- **[Rehype](https://github.com/rehypejs/rehype/)**: processes the Hypertext AST.
:::tip
Use plugins to introduce shorter syntax for the most commonly used JSX elements in your project. The [admonition](./markdown-features-admonitions.mdx) syntax that we offer is also generated by a Remark plugin, and you could do the same for your own use case.
:::
## Default plugins {#default-plugins}
Docusaurus injects [some default Remark plugins](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-mdx-loader/src/remark) during Markdown processing. These plugins would:
- Generate the table of contents;
- Add anchor links to each heading;
- Transform images and links to `require()` calls.
- …
These are all typical use-cases of Remark plugins, which can also be a source of inspiration if you want to implement your own plugin.
## Installing plugins {#installing-plugins}
An MDX plugin is usually an npm package, so you install them like other npm packages using npm. Take the [math plugins](./markdown-features-math-equations.mdx) as an example.
```bash npm2yarn
npm install --save remark-math@5 rehype-katex@6
```
<details>
<summary>How are <code>remark-math</code> and <code>rehype-katex</code> different?</summary>
In case you are wondering how Remark and Rehype are different, here is a good example. `remark-math` operates on the Markdown AST, where it sees text like `$...$`, and all it does is transform that to the JSX `<span class="math math-inline">...</span>` without doing too much with the content. This decouples the extraction of math formulae from their rendering, which means you can swap $\KaTeX$ out with other math renderers, like MathJax (with [`rehype-mathjax`](https://github.com/remarkjs/remark-math/tree/main/packages/rehype-mathjax)), just by replacing the Rehype plugin.
Next, the `rehype-katex` operates on the Hypertext AST where everything has been converted to HTML-like tags already. It traverses all the elements with `math` class and uses $\KaTeX$ to parse and render the content to actual HTML.
</details>
:::warning
Many official Remark/Rehype plugins are [**ES Modules only**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c), a JavaScript module system, which Docusaurus supports. We recommend using an [**ES Modules**](https://flaviocopes.com/es-modules/) config file to make it easier to import such packages.
:::
Next, import your plugins and add them to the plugin options through plugin or preset config in `docusaurus.config.js`:
```js title="docusaurus.config.js"
// highlight-start
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
// highlight-end
// highlight-start
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [remarkMath],
rehypePlugins: [rehypeKatex],
// highlight-end
},
},
],
],
};
```
<details>
<summary>Using a [**CommonJS**](https://nodejs.org/api/modules.html#modules-commonjs-modules) config file?</summary>
If you decide to use a CommonJS config file, it is possible to load those ES module plugins thanks to dynamic imports and an async config creator function:
```js title="docusaurus.config.js"
// highlight-start
module.exports = async function createConfigAsync() {
// highlight-end
return {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [(await import('remark-math')).default],
rehypePlugins: [(await import('rehype-katex')).default],
// highlight-end
},
},
],
],
};
};
```
</details>
## Configuring plugins {#configuring-plugins}
Some plugins can be configured and accept their own options. In that case, use the `[plugin, pluginOptions]` syntax, like this:
```js title="docusaurus.config.js"
import rehypeKatex from 'rehype-katex';
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
rehypePlugins: [
// highlight-start
[rehypeKatex, {strict: false}],
// highlight-end
],
},
},
],
],
};
```
You should check your plugin's documentation for the options it supports.
## Creating new rehype/remark plugins {#creating-new-rehyperemark-plugins}
If there isn't an existing package that satisfies your customization need, you can create your own MDX plugin.
:::note
The writeup below is **not** meant to be a comprehensive guide to creating a plugin, but just an illustration of how to make it work with Docusaurus. Visit the [Remark](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#create-plugins) or [Rehype](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#create-plugins) documentation for a more in-depth explanation of how they work.
:::
For example, let's make a plugin that visits every `h2` heading and adds a `Section X. ` prefix. First, create your plugin source file anywhere—you can even publish it as a separate npm package and install it like explained above. We would put ours at `src/remark/section-prefix.js`. A remark/rehype plugin is just a function that receives the `options` and returns a `transformer` that operates on the AST.
```js "src/remark/section-prefix.js"
import {visit} from 'unist-util-visit';
const plugin = (options) => {
const transformer = async (ast) => {
let number = 1;
visit(ast, 'heading', (node) => {
if (node.depth === 2 && node.children.length > 0) {
node.children.unshift({
type: 'text',
value: `Section ${number}. `,
});
number++;
}
});
};
return transformer;
};
export default plugin;
```
You can now import your plugin in `docusaurus.config.js` and use it just like an installed plugin!
```js title="docusaurus.config.js"
// highlight-next-line
import sectionPrefix from './src/remark/section-prefix';
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
remarkPlugins: [sectionPrefix],
},
},
],
],
};
```
:::tip
The `transformer` has a second parameter [`vfile`](https://github.com/vfile/vfile) which is useful if you need to access the current Markdown file's path.
```js
const plugin = (options) => {
const transformer = async (ast, vfile) => {
ast.children.unshift({
type: 'text',
value: `The current file path is ${vfile.path}`,
});
};
return transformer;
};
```
Our `transformImage` plugin uses this parameter, for example, to transform relative image references to `require()` calls.
:::
:::note
The default plugins of Docusaurus would operate before the custom remark plugins, and that means the images or links have been converted to JSX with `require()` calls already. For example, in the example above, the table of contents generated is still the same even when all `h2` headings are now prefixed by `Section X.`, because the TOC-generating plugin is called before our custom plugin. If you need to process the MDAST before the default plugins do, use the `beforeDefaultRemarkPlugins` and `beforeDefaultRehypePlugins`.
```js title="docusaurus.config.js"
export default {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
beforeDefaultRemarkPlugins: [sectionPrefix],
},
},
],
],
};
```
This would make the table of contents generated contain the `Section X.` prefix as well.
:::

View file

@ -0,0 +1,379 @@
---
id: react
description: Using the power of React in Docusaurus Markdown documents, thanks to MDX
slug: /markdown-features/react
---
# MDX and React
```mdx-code-block
import BrowserWindow from '@site/src/components/BrowserWindow';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import styles from './markdown-features-react.module.css';
```
Docusaurus has built-in support for [MDX](https://mdxjs.com/), which allows you to write JSX within your Markdown files and render them as React components.
Check out the [MDX docs](https://mdxjs.com/) to see what fancy stuff you can do with MDX.
:::tip Debugging MDX
The MDX format is quite strict, and you may get compilation errors.
Use the **[MDX playground](https://mdxjs.com/playground/)** to debug them and make sure your syntax is valid.
:::
:::info
Prettier, the most popular formatter, [supports only the legacy MDX v1](https://github.com/prettier/prettier/issues/12209). If you get an unintentional formatting result, you may want to add `{/* prettier-ignore */}` before the problematic area, or add `*.mdx` to your `.prettierignore`, until Prettier has proper support for MDX v3. [One of the main authors of MDX recommends `remark-cli` with `remark-mdx`](https://github.com/orgs/mdx-js/discussions/2067).
:::
### Exporting components {#exporting-components}
To define any custom component within an MDX file, you have to export it: only paragraphs that start with `export` will be parsed as components instead of prose.
```jsx
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
<Highlight color="#25c2a0">Docusaurus green</Highlight> and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.
I can write **Markdown** alongside my _JSX_!
```
Notice how it renders both the markup from your React component and the Markdown syntax:
```mdx-code-block
export const Highlight = ({children, color}) => (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
<BrowserWindow minHeight={240}>
<><Highlight color="#25c2a0">Docusaurus green</Highlight>
{` `}and <Highlight color="#1877F2">Facebook blue</Highlight> are my favorite colors.</>
I can write **Markdown** alongside my _JSX_!
</BrowserWindow>
```
:::warning MDX is JSX
Since all doc files are parsed using MDX, anything that looks like HTML is actually JSX. Therefore, if you need to inline-style a component, follow JSX flavor and provide style objects.
{/* prettier-ignore */}
```jsx
/* Instead of this: */
<span style="background-color: red">Foo</span>
/* Use this: */
<span style={{backgroundColor: 'red'}}>Foo</span>
```
:::
### Importing components {#importing-components}
You can also import your own components defined in other files or third-party components installed via npm.
{/* prettier-ignore */}
```md
<!-- Docusaurus theme component -->
import TOCInline from '@theme/TOCInline';
<!-- External component -->
import Button from '@mui/material/Button';
<!-- Custom component -->
import BrowserWindow from '@site/src/components/BrowserWindow';
```
:::tip
The `@site` alias points to your website's directory, usually where the `docusaurus.config.js` file is. Using an alias instead of relative paths (`'../../src/components/BrowserWindow'`) saves you from updating import paths when moving files around, or when [versioning docs](../docs/versioning.mdx) and [translating](../../i18n/i18n-tutorial.mdx).
:::
While declaring components within Markdown is very convenient for simple cases, it becomes hard to maintain because of limited editor support, risks of parsing errors, and low reusability. Use a separate `.js` file when your component involves complex JS logic:
```jsx title="src/components/Highlight.js"
import React from 'react';
export default function Highlight({children, color}) {
return (
<span
style={{
backgroundColor: color,
borderRadius: '2px',
color: '#fff',
padding: '0.2rem',
}}>
{children}
</span>
);
}
```
```md title="markdown-file.mdx"
import Highlight from '@site/src/components/Highlight';
<Highlight color="#25c2a0">Docusaurus green</Highlight>
```
:::tip
If you use the same component across a lot of files, you don't need to import it everywhere—consider adding it to the global scope. [See below](#mdx-component-scope)
:::
### MDX component scope {#mdx-component-scope}
Apart from [importing a component](#importing-components) and [exporting a component](#exporting-components), a third way to use a component in MDX is to **register it to the global scope**, which will make it automatically available in every MDX file, without any import statements.
For example, given this MDX file:
```md
- a
- list!
And some <Highlight>custom markup</Highlight>...
```
It will be compiled to a React component containing `ul`, `li`, `p`, and `Highlight` elements. `Highlight` is not a native html element: you need to provide your own React component implementation for it.
In Docusaurus, the MDX component scope is provided by the `@theme/MDXComponents` file. It's not a React component, _per se_, unlike most other exports under the `@theme/` alias: it is a record from tag names like `Highlight` to their React component implementations.
If you [swizzle](../../swizzling.mdx) this component, you will find all tags that have been implemented, and you can further customize our implementation by swizzling the respective sub-component, like `@theme/MDXComponents/Code` (which is used to render [Markdown code blocks](./markdown-features-code-blocks.mdx)).
If you want to register extra tag names (like the `<Highlight>` tag above), you should consider [wrapping `@theme/MDXComponents`](../../swizzling.mdx#wrapping), so you don't have to maintain all the existing mappings. Since the swizzle CLI doesn't allow wrapping non-component files yet, you should manually create the wrapper:
```js title="src/theme/MDXComponents.js"
import React from 'react';
// Import the original mapper
import MDXComponents from '@theme-original/MDXComponents';
// highlight-next-line
import Highlight from '@site/src/components/Highlight';
export default {
// Re-use the default mapping
...MDXComponents,
// Map the "<Highlight>" tag to our Highlight component
// `Highlight` will receive all props that were passed to `<Highlight>` in MDX
// highlight-next-line
Highlight,
};
```
And now, you can freely use `<Highlight>` in every page, without writing the import statement:
```md
I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere!
```
```mdx-code-block
<BrowserWindow>
I can conveniently use <Highlight color="#25c2a0">Docusaurus green</Highlight> everywhere!
</BrowserWindow>
```
:::warning
We use **upper-case** tag names like `Highlight` on purpose.
From MDX v3+ onward (Docusaurus v3+), lower-case tag names are always rendered as native html elements, and will not use any component mapping you provide.
:::
:::warning
This feature is powered by [an `MDXProvider`](https://mdxjs.com/docs/using-mdx/#mdx-provider). If you are importing Markdown in a React page, you have to supply this provider yourself through the `MDXContent` theme component.
```jsx title="src/pages/index.js"
import React from 'react';
import FeatureDisplay from './_featureDisplay.mdx';
// highlight-next-line
import MDXContent from '@theme/MDXContent';
export default function LandingPage() {
return (
<div>
{/* highlight-start */}
<MDXContent>
<FeatureDisplay />
</MDXContent>
{/* highlight-end */}
</div>
);
}
```
If you don't wrap your imported MDX with `MDXContent`, the global scope will not be available.
:::
### Markdown and JSX interoperability {#markdown-and-jsx-interoperability}
Docusaurus v3 is using [MDX v3](https://mdxjs.com/blog/v3/).
The [MDX syntax](https://mdxjs.com/docs/what-is-mdx/#mdx-syntax) is mostly compatible with [CommonMark](https://commonmark.org/), but is much stricter because your `.mdx` files can use JSX and are compiled into real React components (check the [playground](https://mdxjs.com/playground/)).
Some valid CommonMark features won't work with MDX ([more info](https://mdxjs.com/docs/what-is-mdx/#markdown)), notably:
- Indented code blocks: use triple backticks instead
- Autolinks (`<http://localhost:3000>`): use regular link syntax instead (`[http://localhost:3000](http://localhost:3000)`)
- HTML syntax (`<p style="color: red;">`): use JSX instead (`<p style={{color: 'red'}}>`)
- Unescaped `{` and `<`: escape them with `\` instead (`\{` and `\<`)
:::danger Experimental CommonMark support
Docusaurus v3 makes it possible to opt-in for a less strict, standard [CommonMark](https://commonmark.org/) support with the following options:
- The `format: md` front matter
- The `.md` file extension combined with the `siteConfig.markdown.format: "detect"` configuration
This feature is **experimental** and currently has a few [limitations](https://github.com/facebook/docusaurus/issues/9092).
:::
## Importing code snippets {#importing-code-snippets}
You can not only import a file containing a component definition, but also import any code file as raw text, and then insert it in a code block, thanks to [Webpack raw-loader](https://webpack.js.org/loaders/raw-loader/). In order to use `raw-loader`, you first need to install it in your project:
```bash npm2yarn
npm install --save raw-loader
```
Now you can import code snippets from another file as it is:
{/* prettier-ignore */}
```jsx title="myMarkdownFile.mdx"
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
```
```mdx-code-block
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!@site/src/pages/examples/_myComponent';
<BrowserWindow>
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
</BrowserWindow>
```
See [using code blocks in JSX](./markdown-features-code-blocks.mdx#usage-in-jsx) for more details of the `<CodeBlock>` component.
:::note
You have to use `<CodeBlock>` rather than the Markdown triple-backtick ` ``` `, because the latter will ship out any of its content as-is, but you want to interpolate the imported text here.
:::
:::warning
This feature is experimental and might be subject to breaking API changes in the future.
:::
## Importing Markdown {#importing-markdown}
You can use Markdown files as components and import them elsewhere, either in Markdown files or in React pages.
By convention, using the **`_` filename prefix** will not create any doc page and means the Markdown file is a **"partial"**, to be imported by other files.
```md title="_markdown-partial-example.mdx"
<span>Hello {props.name}</span>
This is text some content from `_markdown-partial-example.mdx`.
```
{/* prettier-ignore */}
```jsx title="someOtherDoc.mdx"
import PartialExample from './_markdown-partial-example.mdx';
<PartialExample name="Sebastien" />
```
```mdx-code-block
import PartialExample from './_markdown-partial-example.mdx';
<BrowserWindow>
<PartialExample name="Sebastien" />
</BrowserWindow>
```
This way, you can reuse content among multiple pages and avoid duplicating materials.
:::warning
Currently, the table of contents does not contain the imported Markdown headings. This is a technical limitation that we are trying to solve ([issue](https://github.com/facebook/docusaurus/issues/3915)).
:::
## Available exports {#available-exports}
Within the MDX page, the following variables are available as globals:
- `frontMatter`: the front matter as a record of string keys and values;
- `toc`: the table of contents, as a tree of headings. See also [Inline TOC](./markdown-features-toc.mdx#inline-table-of-contents) for a more concrete use-case.
- `contentTitle`: the Markdown title, which is the first `h1` heading in the Markdown text. It's `undefined` if there isn't one (e.g. title specified in the front matter).
```jsx
import TOCInline from '@theme/TOCInline';
import CodeBlock from '@theme/CodeBlock';
The table of contents for this page, serialized:
<CodeBlock className="language-json">{JSON.stringify(toc, null, 2)}</CodeBlock>
The front matter of this page:
<ul>
{Object.entries(frontMatter).map(([key, value]) => <li key={key}><b>{key}</b>: {value}</li>)}
</ul>
<p>The title of this page is: <b>{contentTitle}</b></p>
```
```mdx-code-block
import TOCInline from '@theme/TOCInline';
<BrowserWindow>
The table of contents for this page, serialized:
<CodeBlock className="language-json">{JSON.stringify(toc, null, 2)}</CodeBlock>
The front matter of this page:
<ul>
{Object.entries(frontMatter).map(([key, value]) => <li key={key}><b>{key}</b>: {value}</li>)}
</ul>
<p>The title of this page is: <b>{contentTitle}</b></p>
</BrowserWindow>
```

View file

@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
.wrappingBlock {
width: 50%;
display: inline-block;
padding: 5px;
vertical-align: top;
}
.wrappingBlock code[class^='codeBlockLines'] {
white-space: pre-wrap;
}

View file

@ -0,0 +1,30 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
.red {
color: red;
}
.red[aria-selected='true'] {
border-bottom-color: red;
}
.orange {
color: orange;
}
.orange[aria-selected='true'] {
border-bottom-color: orange;
}
.yellow {
color: yellow;
}
.yellow[aria-selected='true'] {
border-bottom-color: yellow;
}

View file

@ -0,0 +1,378 @@
---
id: tabs
description: Using tabs inside Docusaurus Markdown
slug: /markdown-features/tabs
---
# Tabs
```mdx-code-block
import BrowserWindow from '@site/src/components/BrowserWindow';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import styles from './markdown-features-tabs-styles.module.css';
```
Docusaurus provides the `<Tabs>` component that you can use in Markdown thanks to [MDX](./markdown-features-react.mdx):
{/* prettier-ignore */}
```jsx
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="apple" label="Apple" default>
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange">
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana">
This is a banana 🍌
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs>
<TabItem value="apple" label="Apple">This is an apple 🍎</TabItem>
<TabItem value="orange" label="Orange">This is an orange 🍊</TabItem>
<TabItem value="banana" label="Banana">This is a banana 🍌</TabItem>
</Tabs>
</BrowserWindow>
```
---
It is also possible to provide `values` and `defaultValue` props to `Tabs`:
```jsx
<Tabs
defaultValue="apple"
values={[
{label: 'Apple', value: 'apple'},
{label: 'Orange', value: 'orange'},
{label: 'Banana', value: 'banana'},
]}>
<TabItem value="apple">This is an apple 🍎</TabItem>
<TabItem value="orange">This is an orange 🍊</TabItem>
<TabItem value="banana">This is a banana 🍌</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs
defaultValue="apple"
values={[
{label: 'Apple', value: 'apple'},
{label: 'Orange', value: 'orange'},
{label: 'Banana', value: 'banana'},
]}>
<TabItem value="apple">This is an apple 🍎</TabItem>
<TabItem value="orange">This is an orange 🍊</TabItem>
<TabItem value="banana">This is a banana 🍌</TabItem>
</Tabs>
</BrowserWindow>
```
<details>
<summary><code>Tabs</code> props take precedence over the <code>TabItem</code> props:</summary>
```jsx
<Tabs
defaultValue="apple"
values={[
{label: 'Apple 1', value: 'apple'},
{label: 'Orange 1', value: 'orange'},
{label: 'Banana 1', value: 'banana'},
]}>
<TabItem value="apple" label="Apple 2">
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange 2">
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana 2" default>
This is a banana 🍌
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs
defaultValue="apple"
values={[
{label: 'Apple 1', value: 'apple'},
{label: 'Orange 1', value: 'orange'},
{label: 'Banana 1', value: 'banana'},
]}>
<TabItem value="apple" label="Apple 2">This is an apple 🍎</TabItem>
<TabItem value="orange" label="Orange 2">This is an orange 🍊</TabItem>
<TabItem value="banana" label="Banana 2" default>This is a banana 🍌</TabItem>
</Tabs>
</BrowserWindow>
```
</details>
:::tip
By default, all tabs are rendered eagerly during the build process, and search engines can index hidden tabs.
It is possible to only render the default tab with `<Tabs lazy />`.
:::
## Displaying a default tab {#displaying-a-default-tab}
The first tab is displayed by default, and to override this behavior, you can specify a default tab by adding `default` to one of the tab items. You can also set the `defaultValue` prop of the `Tabs` component to the label value of your choice. For example, in the example above, either setting `default` for the `value="apple"` tab or setting `defaultValue="apple"` for the tabs forces the "Apple" tab to be open by default.
Docusaurus will throw an error if a `defaultValue` is provided for the `Tabs` but it refers to a non-existing value. If you want none of the tabs to be shown by default, use `defaultValue={null}`.
## Syncing tab choices {#syncing-tab-choices}
You may want choices of the same kind of tabs to sync with each other. For example, you might want to provide different instructions for users on Windows vs users on macOS, and you want to change all OS-specific instructions tabs in one click. To achieve that, you can give all related tabs the same `groupId` prop. Note that doing this will persist the choice in `localStorage` and all `<Tab>` instances with the same `groupId` will update automatically when the value of one of them is changed. Note that group IDs are globally namespaced.
```jsx
// highlight-next-line
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Use Ctrl + C to copy.</TabItem>
<TabItem value="mac" label="macOS">Use Command + C to copy.</TabItem>
</Tabs>
// highlight-next-line
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Use Ctrl + V to paste.</TabItem>
<TabItem value="mac" label="macOS">Use Command + V to paste.</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Use Ctrl + C to copy.</TabItem>
<TabItem value="mac" label="macOS">Use Command + C to copy.</TabItem>
</Tabs>
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Use Ctrl + V to paste.</TabItem>
<TabItem value="mac" label="macOS">Use Command + V to paste.</TabItem>
</Tabs>
</BrowserWindow>
```
For all tab groups that have the same `groupId`, the possible values do not need to be the same. If one tab group is chosen a value that does not exist in another tab group with the same `groupId`, the tab group with the missing value won't change its tab. You can see that from the following example. Try to select Linux, and the above tab groups don't change.
```jsx
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">
I am Windows.
</TabItem>
<TabItem value="mac" label="macOS">
I am macOS.
</TabItem>
<TabItem value="linux" label="Linux">
I am Linux.
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">I am Windows.</TabItem>
<TabItem value="mac" label="macOS">I am macOS.</TabItem>
<TabItem value="linux" label="Linux">I am Linux.</TabItem>
</Tabs>
</BrowserWindow>
```
---
Tab choices with different group IDs will not interfere with each other:
```jsx
// highlight-next-line
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Windows in windows.</TabItem>
<TabItem value="mac" label="macOS">macOS is macOS.</TabItem>
</Tabs>
// highlight-next-line
<Tabs groupId="non-mac-operating-systems">
<TabItem value="win" label="Windows">Windows is windows.</TabItem>
<TabItem value="unix" label="Unix">Unix is unix.</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs groupId="operating-systems">
<TabItem value="win" label="Windows">Windows in windows.</TabItem>
<TabItem value="mac" label="macOS">macOS is macOS.</TabItem>
</Tabs>
<Tabs groupId="non-mac-operating-systems">
<TabItem value="win" label="Windows">Windows is windows.</TabItem>
<TabItem value="unix" label="Unix">Unix is unix.</TabItem>
</Tabs>
</BrowserWindow>
```
## Customizing tabs {#customizing-tabs}
You might want to customize the appearance of a certain set of tabs. You can pass the string in `className` prop, and the specified CSS class will be added to the `Tabs` component:
```jsx
// highlight-next-line
<Tabs className="unique-tabs">
<TabItem value="Apple">This is an apple 🍎</TabItem>
<TabItem value="Orange">This is an orange 🍊</TabItem>
<TabItem value="Banana">This is a banana 🍌</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs className="unique-tabs">
<TabItem value="Apple">This is an apple 🍎</TabItem>
<TabItem value="Orange">This is an orange 🍊</TabItem>
<TabItem value="Banana">This is a banana 🍌</TabItem>
</Tabs>
</BrowserWindow>
```
### Customizing tab headings {#customizing-tab-headings}
You can also customize each tab heading independently by using the `attributes` field. The extra props can be passed to the headings either through the `values` prop in `Tabs`, or props of each `TabItem`—in the same way as you declare `label`.
{/* prettier-ignore */}
```jsx title="some-doc.mdx"
import styles from './styles.module.css';
<Tabs>
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
This is a banana 🍌
</TabItem>
</Tabs>
```
```css title="styles.module.css"
.red {
color: red;
}
.red[aria-selected='true'] {
border-bottom-color: red;
}
.orange {
color: orange;
}
.orange[aria-selected='true'] {
border-bottom-color: orange;
}
.yellow {
color: yellow;
}
.yellow[aria-selected='true'] {
border-bottom-color: yellow;
}
```
```mdx-code-block
<BrowserWindow>
<Tabs>
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
This is a banana 🍌
</TabItem>
</Tabs>
</BrowserWindow>
```
:::tip
`className` would be merged with other default class names. You may also use a custom `data-value` field (`{'data-value': 'apple'}`) paired with CSS attribute selectors:
```css title="styles.module.css"
li[role='tab'][data-value='apple'] {
color: red;
}
```
:::
## Query string {#query-string}
It is possible to persist the selected tab into the url search parameters. This enables you to share a link to a page which pre-selects the tab - linking from your Android app to documentation with the Android tabs pre-selected. This feature does not provide an anchor link - the browser will not scroll to the tab.
Use the `queryString` prop to enable this feature and define the search param name to use.
```tsx
// highlight-next-line
<Tabs queryString="current-os">
<TabItem value="android" label="Android">
Android
</TabItem>
<TabItem value="ios" label="iOS">
iOS
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs queryString='current-os'>
<TabItem value="android" label="Android">Android</TabItem>
<TabItem value="ios" label="iOS">iOS</TabItem>
</Tabs>
</BrowserWindow>
```
As soon as a tab is clicked, a search parameter is added at the end of the url: `?current-os=android` or `?current-os=ios`.
:::tip
`queryString` can be used together with `groupId`.
For convenience, when the `queryString` prop is `true`, the `groupId` value will be used as a fallback.
```tsx
// highlight-next-line
<Tabs groupId="current-os" queryString>
<TabItem value="android" label="Android">
Android
</TabItem>
<TabItem value="ios" label="iOS">
iOS
</TabItem>
</Tabs>
```
```mdx-code-block
<BrowserWindow>
<Tabs queryString groupId="current-os">
<TabItem value="android" label="Android">Android</TabItem>
<TabItem value="ios" label="iOS">iOS</TabItem>
</Tabs>
</BrowserWindow>
```
When the page loads, the tab query string choice will be restored in priority over the `groupId` choice (using `localStorage`).
:::

View file

@ -0,0 +1,283 @@
---
id: toc
description: Customizing headings and table-of-contents in Markdown
slug: /markdown-features/toc
---
import BrowserWindow from '@site/src/components/BrowserWindow';
# Headings and Table of contents
## Markdown headings {#markdown-headings}
You can use regular Markdown headings.
```md
## Level 2 title
### Level 3 title
#### Level 4 title
```
Each Markdown heading will appear as a table of contents entry.
### Heading IDs {#heading-ids}
Each heading has an ID that can be automatically generated or explicitly specified. Heading IDs allow you to link to a specific document heading in Markdown or JSX:
```md
[link](#heading-id)
```
```jsx
<Link to="#heading-id">link</Link>
```
By default, Docusaurus will generate heading IDs for you, based on the heading text. For example, `### Hello World` will have ID `hello-world`.
Generated IDs have **some limitations**:
- The ID might not look good
- You might want to **change or translate** the text without updating the existing ID
A special Markdown syntax lets you set an **explicit heading id**:
```mdx-code-block
<Code language="md">{'### Hello World \u007B#my-explicit-id}\n'}</Code>
```
:::tip
Use the **[`write-heading-ids`](../../cli.mdx#docusaurus-write-heading-ids-sitedir)** CLI command to add explicit IDs to all your Markdown documents.
:::
:::warning Avoid colliding IDs
Generated heading IDs will be guaranteed to be unique on each page, but if you use custom IDs, make sure each one appears exactly once on each page, or there will be two DOM elements with the same ID, which is invalid HTML semantics, and will lead to one heading being unlinkable.
:::
## Table of contents heading level {#table-of-contents-heading-level}
Each Markdown document displays a table of contents on the top-right corner. By default, this table only shows h2 and h3 headings, which should be sufficient for an overview of the page structure. In case you need to change the range of headings displayed, you can customize the minimum and maximum heading level — either per page or globally.
To set the heading level for a particular page, use the `toc_min_heading_level` and `toc_max_heading_level` front matter.
```md title="myDoc.md"
---
# Display h2 to h5 headings
toc_min_heading_level: 2
toc_max_heading_level: 5
---
```
To set the heading level for _all_ pages, use the [`themeConfig.tableOfContents`](../../api/themes/theme-configuration.mdx#table-of-contents) option.
```js title="docusaurus.config.js"
export default {
themeConfig: {
tableOfContents: {
// highlight-start
minHeadingLevel: 2,
maxHeadingLevel: 5,
// highlight-end
},
},
};
```
If you've set the options globally, you can still override them locally via front matter.
:::note
The `themeConfig` option would apply to all TOC on the site, including [inline TOC](#inline-table-of-contents), but front matter options only affect the top-right TOC. You need to use the `minHeadingLevel` and `maxHeadingLevel` props to customize each `<TOCInline />` component.
:::
## Inline table of contents {#inline-table-of-contents}
It is also possible to display an inline table of contents directly inside a Markdown document, thanks to MDX.
The `toc` variable is available in any MDX document and contains all the headings of an MDX document. By default, only `h2` and `h3` headings are displayed in the TOC. You can change which heading levels are visible by setting `minHeadingLevel` or `maxHeadingLevel` for individual `TOCInline` components.
{/* prettier-ignore */}
```jsx
import TOCInline from '@theme/TOCInline';
<TOCInline toc={toc} />
```
```mdx-code-block
import TOCInline from '@theme/TOCInline';
<BrowserWindow>
<TOCInline toc={toc} />
</BrowserWindow>
```
The `toc` global is just a list of heading items:
```ts
declare const toc: {
value: string;
id: string;
level: number;
}[];
```
Note that the `toc` global is a flat array, so you can easily cut out unwanted nodes or insert extra nodes, and create a new TOC tree.
{/* prettier-ignore */}
```jsx
import TOCInline from '@theme/TOCInline';
<TOCInline
// Only show h2 and h4 headings
toc={toc.filter((node) => node.level === 2 || node.level === 4)}
minHeadingLevel={2}
// Show h4 headings in addition to the default h2 and h3 headings
maxHeadingLevel={4}
/>
```
```mdx-code-block
<BrowserWindow>
<TOCInline
toc={toc.filter((node) => node.level === 2 || node.level === 4)}
minHeadingLevel={2}
maxHeadingLevel={4}
/>
</BrowserWindow>
```
## Customizing table of contents generation {#customizing-table-of-contents-generation}
The table-of-contents is generated by parsing the Markdown source with a [Remark plugin](./markdown-features-plugins.mdx). There are known edge-cases where it generates false-positives and false-negatives.
Markdown headings within hideable areas will still show up in the TOC. For example, headings within [`Tabs`](./markdown-features-tabs.mdx) and [`details`](./markdown-features-intro.mdx#details) will not be excluded.
Non-Markdown headings will not show up in the TOC. This can be used to your advantage to tackle the aforementioned issue.
```md
<details>
<summary>Some details containing headings</summary>
<h2 id="#heading-id">I'm a heading that will not show up in the TOC</h2>
Some content...
</details>
```
The ability to ergonomically insert extra headings or ignore certain headings is a work-in-progress. If this feature is important to you, please report your use-case in [this issue](https://github.com/facebook/docusaurus/issues/6201).
---
:::warning
Below is just some dummy content to have more table of contents items available on the current page.
:::
## Example Section 1 {#example-section-1}
Lorem ipsum
### Example Subsection 1 a {#example-subsection-1-a}
Lorem ipsum
#### Example subsubsection 1 a I
#### Example subsubsection 1 a II
#### Example subsubsection 1 a III
### Example Subsection 1 b {#example-subsection-1-b}
Lorem ipsum
#### Example subsubsection 1 b I
#### Example subsubsection 1 b II
#### Example subsubsection 1 b III
### Example Subsection 1 c {#example-subsection-1-c}
Lorem ipsum
#### Example subsubsection 1 c I
#### Example subsubsection 1 c II
#### Example subsubsection 1 c III
## Example Section 2 {#example-section-2}
Lorem ipsum
### Example Subsection 2 a {#example-subsection-2-a}
Lorem ipsum
#### Example subsubsection 2 a I
#### Example subsubsection 2 a II
#### Example subsubsection 2 a III
### Example Subsection 2 b {#example-subsection-2-b}
Lorem ipsum
#### Example subsubsection 2 b I
#### Example subsubsection 2 b II
#### Example subsubsection 2 b III
### Example Subsection 2 c {#example-subsection-2-c}
Lorem ipsum
#### Example subsubsection 2 c I
#### Example subsubsection 2 c II
#### Example subsubsection 2 c III
## Example Section 3 {#example-section-3}
Lorem ipsum
### Example Subsection 3 a {#example-subsection-3-a}
Lorem ipsum
#### Example subsubsection 3 a I
#### Example subsubsection 3 a II
#### Example subsubsection 3 a III
### Example Subsection 3 b {#example-subsection-3-b}
Lorem ipsum
#### Example subsubsection 3 b I
#### Example subsubsection 3 b II
#### Example subsubsection 3 b III
### Example Subsection 3 c {#example-subsection-3-c}
Lorem ipsum
#### Example subsubsection 3 c I
#### Example subsubsection 3 c II
#### Example subsubsection 3 c III

View file

@ -0,0 +1,21 @@
# What's next?
Congratulations! You have understood most core features of Docusaurus now. You have:
- [Used the pages plugin](./creating-pages.mdx) to create a standalone React / Markdown page
- [Used the docs plugin](./docs/docs-introduction.mdx) to create documentation pages. This includes [configuring the sidebar](./docs/sidebar/index.mdx), and even [versioning](./docs/versioning.mdx)
- [Used the blog plugin](../blog.mdx) to create a fully featured blog
- Tried your hands on [a range of Markdown features](./markdown-features/markdown-features-intro.mdx), which are useful for all content plugins
- [Used stylesheets](../styling-layout.mdx) or [swizzling](../swizzling.mdx) to customize your site's appearance
- [Included images and other assets](../static-assets.mdx) in your pages
- [Added search](../search.mdx) to your site
- Understood how [browser support](../browser-support.mdx) and [SEO](../seo.mdx) are done through standard Docusaurus APIs
- Learned about how [individual plugins](../using-plugins.mdx) are installed and configured
- [Deployed](../deployment.mdx) your site to a content host
- [Internationalized](../i18n/i18n-tutorial.mdx) your site to include multiple languages
At this point, you probably have a big `docusaurus.config.js` already😄 However, you haven't written much code yet! Most of the features are implemented through calling encapsulated Docusaurus APIs. As you continue your journey, you can take three paths:
- Learn more advanced Docusaurus concepts. This will help you gain a deeper understand of what the APIs do.
- Read about [all existing APIs](../docusaurus-core.mdx). Many of them have not been covered in the Guides!
- Learn to [develop a plugin](../api/plugin-methods/README.mdx) to extend the functionality of your site.