docs: create Docusaurus v2.4.0 release docs + changelog + release blog post (#8811)

This commit is contained in:
Sébastien Lorber 2023-03-23 19:32:09 +01:00 committed by GitHub
parent da9f86409d
commit 97caab16d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
105 changed files with 19157 additions and 22 deletions

View file

@ -1,5 +1,69 @@
# Docusaurus 2 Changelog
## 2.4.0 (2023-03-23)
#### :rocket: New Feature
- `docusaurus-plugin-content-docs`, `docusaurus-theme-classic`
- [#8236](https://github.com/facebook/docusaurus/pull/8236) feat(content-docs): add support for sidebar item category/link descriptions in generated index page ([@ZarakiKanzaki](https://github.com/ZarakiKanzaki))
- `docusaurus-theme-classic`
- [#8708](https://github.com/facebook/docusaurus/pull/8708) feat(theme): allow to load a Docusaurus page with theme from query-string: ?docusaurus-theme=dark ([@slorber](https://github.com/slorber))
- [#8616](https://github.com/facebook/docusaurus/pull/8616) feat(theme): add ability to translate navbar+footer logo alt text ([@Mysterious-Dev](https://github.com/Mysterious-Dev))
- `docusaurus-remark-plugin-npm2yarn`
- [#8690](https://github.com/facebook/docusaurus/pull/8690) feat(npm-to-yarn): add support for PnPm and custom converters ([@armano2](https://github.com/armano2))
- `docusaurus`
- [#8677](https://github.com/facebook/docusaurus/pull/8677) feat(core): add script env variables: NODE_ENV + BABEL_ENV + DOCUSAURUS_CURRENT_LOCALE (temporary i18n workaround) ([@slorber](https://github.com/slorber))
- `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#8674](https://github.com/facebook/docusaurus/pull/8674) feat(theme-classic): respect `prefers-reduced-motion: reduce` mediaquery, bump Infima to alpha.43 ([@slorber](https://github.com/slorber))
- `docusaurus-theme-translations`
- [#8668](https://github.com/facebook/docusaurus/pull/8668) feat(theme-translations): add Hungarian theme translations ([@trueqap](https://github.com/trueqap))
- [#8631](https://github.com/facebook/docusaurus/pull/8631) feat(theme-translations): add Norwegian (Bokmål) theme translation ([@dr0nn1](https://github.com/dr0nn1))
- `docusaurus-theme-common`
- [#8656](https://github.com/facebook/docusaurus/pull/8656) feat(theme-common): allow passing a string for details summary ([@pReya](https://github.com/pReya))
- `docusaurus-plugin-google-gtag`
- [#8620](https://github.com/facebook/docusaurus/pull/8620) feat(gtag-plugin): gtag should support multiple tracking ids, notably for the UA => GA4 transition ([@slorber](https://github.com/slorber))
#### :bug: Bug Fix
- `docusaurus-theme-classic`
- [#8803](https://github.com/facebook/docusaurus/pull/8803) fix(theme): codeblock buttons should be kept on the right when using RTL locale ([@Vishruta-Patil](https://github.com/Vishruta-Patil))
- [#8615](https://github.com/facebook/docusaurus/pull/8615) fix(theme): improve color toggle when using dark navbar ([@dewanshDT](https://github.com/dewanshDT))
- [#8699](https://github.com/facebook/docusaurus/pull/8699) fix(theme-classic): fix tab focus bug in dropdown (#8697) ([@kagankan](https://github.com/kagankan))
- `docusaurus-theme-classic`, `docusaurus-theme-common`
- [#8801](https://github.com/facebook/docusaurus/pull/8801) fix(theme): allow tabs children to be falsy ([@Josh-Cena](https://github.com/Josh-Cena))
- `docusaurus-theme-common`, `docusaurus-theme-search-algolia`
- [#8757](https://github.com/facebook/docusaurus/pull/8757) fix(search): search page should react to querystring changes + cleanup/refactor ([@slorber](https://github.com/slorber))
- `docusaurus`
- [#8746](https://github.com/facebook/docusaurus/pull/8746) fix(core): baseUrl error banner link anchor case ([@slorber](https://github.com/slorber))
- `docusaurus-theme-translations`
- [#8744](https://github.com/facebook/docusaurus/pull/8744) fix(theme-translations): fix wrong arabic words (tip/next) ([@Anasqx](https://github.com/Anasqx))
#### :nail_care: Polish
- `create-docusaurus`
- [#8712](https://github.com/facebook/docusaurus/pull/8712) polish(create-docusaurus): the starter template should use a navbar item "docSidebar" instead of "doc" (less fragile on updates) ([@biplavmz](https://github.com/biplavmz))
- `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus-utils-common`, `docusaurus`
- [#8735](https://github.com/facebook/docusaurus/pull/8735) polish(theme): better error messages on navbar item rendering failures + ErrorCauseBoundary API ([@tannerdolby](https://github.com/tannerdolby))
- `docusaurus-theme-classic`, `docusaurus-theme-common`, `docusaurus`
- [#8736](https://github.com/facebook/docusaurus/pull/8736) polish(core): better styling for error screens ([@tannerdolby](https://github.com/tannerdolby))
#### Committers: 14
- Anas ([@Anasqx](https://github.com/Anasqx))
- Armano ([@armano2](https://github.com/armano2))
- Davide Donadio ([@ZarakiKanzaki](https://github.com/ZarakiKanzaki))
- Dewansh Thakur ([@dewanshDT](https://github.com/dewanshDT))
- Joshua Chen ([@Josh-Cena](https://github.com/Josh-Cena))
- Kagan ([@kagankan](https://github.com/kagankan))
- Moritz Stückler ([@pReya](https://github.com/pReya))
- Mysterious_Dev ([@Mysterious-Dev](https://github.com/Mysterious-Dev))
- Petter Drønnen ([@dr0nn1](https://github.com/dr0nn1))
- Sébastien Lorber ([@slorber](https://github.com/slorber))
- Tanner Dolby ([@tannerdolby](https://github.com/tannerdolby))
- TrueQAP ([@trueqap](https://github.com/trueqap))
- Vishruta Patil ([@Vishruta-Patil](https://github.com/Vishruta-Patil))
- [@biplavmz](https://github.com/biplavmz)
## 2.3.1 (2023-02-03)
#### :bug: Bug Fix

View file

@ -25,6 +25,7 @@ beforeinstallprompt
bhatt
blocklist
blockquotes
Bokmål
browserslist
browserstack
buble

View file

@ -8,20 +8,7 @@
import React from 'react';
import Layout from '@theme/Layout';
import Heading from '@theme/Heading';
import BrowserWindow from '@site/src/components/BrowserWindow';
function IframeTest({url}: {url: string}) {
return (
<div style={{padding: 10}}>
<BrowserWindow
url={url}
style={{minWidth: '40vw', maxWidth: 400}}
bodyStyle={{padding: 0}}>
<iframe src={url} title={url} style={{width: '100%', height: 300}} />
</BrowserWindow>
</div>
);
}
import IframeWindow from '@site/src/components/BrowserWindow/IframeWindow';
// See https://github.com/facebook/docusaurus/issues/8672
export default function Embeds(): JSX.Element {
@ -30,12 +17,12 @@ export default function Embeds(): JSX.Element {
<div style={{padding: 10}}>
<Heading as="h1">Test Embeds</Heading>
<div style={{display: 'flex', flexWrap: 'wrap'}}>
<IframeTest url="/?docusaurus-theme=light" />
<IframeTest url="/?docusaurus-theme=dark" />
<IframeTest url="/?docusaurus-theme=unexpected-value" />
<IframeTest url="/" />
<IframeTest url="https://docusaurus.io/" />
<IframeTest url="https://tutorial.docusaurus.io/" />
<IframeWindow url="/?docusaurus-theme=light" />
<IframeWindow url="/?docusaurus-theme=dark" />
<IframeWindow url="/?docusaurus-theme=unexpected-value" />
<IframeWindow url="/" />
<IframeWindow url="https://docusaurus.io/" />
<IframeWindow url="https://tutorial.docusaurus.io/" />
</div>
</div>
</Layout>

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

View file

@ -0,0 +1,161 @@
---
title: Docusaurus 2.4
authors: [slorber]
tags: [release]
image: ./img/social-card.png
date: 2023-03-23
---
We are happy to announce **Docusaurus 2.4**.
The upgrade should be easy: as explained in our [release process documentation](/community/release-process), minor versions respect [Semantic Versioning](https://semver.org/).
![Docusaurus blog post social card](./img/social-card.png)
<!--truncate-->
import BrowserWindow from '@site/src/components/BrowserWindow';
import IframeWindow from '@site/src/components/BrowserWindow/IframeWindow';
import ErrorBoundaryTestButton from '@site/src/components/ErrorBoundaryTestButton';
## Highlights
### Sidebar item description
In [#8236](https://github.com/facebook/docusaurus/pull/8236), we made it possible to provide a new `description` attribute for docs sidebar items of type `link` and `category`.
```tsx title="sidebars.js"
[
{
type: 'link',
label: 'Link with description',
href: 'https://docusaurus.io',
// highlight-next-line
description: 'Some link description',
},
{
type: 'category',
label: 'Category with description',
// highlight-next-line
description: 'Some category description',
items: [],
},
];
```
These descriptions will be used in category generated index pages.
![Show sidebar category generated index with custom descriptions](./img/sidebar-item-description.jpg)
### Theme Query String
In [#8708](https://github.com/facebook/docusaurus/pull/8708), we added the possibility to force Docusaurus to initialize itself in `light` or `dark` mode through a new `docusaurus-theme` query-string parameter.
This is useful to ensure a consistent theme when embedding an existing Docusaurus page into an iframe or WebView.
<IframeWindow url="/docs/?docusaurus-theme=light" />
<IframeWindow url="/docs/?docusaurus-theme=dark" />
### Remark plugin npm2yarn upgrade
In [#8690](https://github.com/facebook/docusaurus/pull/8690), we upgraded our Remark plugin [@docusaurus/remark-plugin-npm2yarn](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-remark-plugin-npm2yarn) with many conversion bug fixes, first-class support for pnpm, and the ability to register custom converters producing new tabs.
````markdown
Run these commands!
```bash npm2yarn
npm install
npm run build
npm run myCustomScript -- --some-arg
```
````
<BrowserWindow>
```bash npm2yarn
npm install
npm run build
npm run myCustomScript -- --some-arg
```
</BrowserWindow>
### gtag support for multiple tracking IDs
In [#8620](https://github.com/facebook/docusaurus/pull/8620) we added support for the [@docusaurus/plugin-google-gtag](/docs/api/plugins/@docusaurus/plugin-google-gtag) plugin to declare multiple tracking IDs.
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
gtag: {
trackingID: [
// highlight-next-line
'G-<YOUR-NEW-GA4-ID>',
// highlight-next-line
'UA-<YOUR-OLD-UA-ID>',
],
},
},
],
],
};
```
:::caution Google is sunsetting Universal Analytics
**[Google will sunset its Universal Analytics](https://blog.google/products/marketingplatform/analytics/prepare-for-future-with-google-analytics-4/)** on **July 1, 2023**.
Docusaurus users should migrate to Google Analytics 4. Google **does not permit to migrate your existing Universal Analytics data** to your new Google Analytics 4 property.
To preserve the continuity of your analytics, we temporarily recommend that you report events to 2 tracking IDs at the same time: the old one (`UA-*`) and new one (`G-*`). Refer to the **[dedicated issue](https://github.com/facebook/docusaurus/issues/7221)** for details.
:::
### Developer Experience
In [#8736](https://github.com/facebook/docusaurus/pull/8736), we improved how we render error messages and added initial support to render the full causal chain of an error (see [ES2022 Error Cause](https://h3manth.com/ES2022/#error-cause)).
:::tip
To see it in action, click here: <ErrorBoundaryTestButton cause="Probably undefined is not a function 😄"/>
:::
In [#8735](https://github.com/facebook/docusaurus/pull/8735) we also made navbar-related error messages clearer to help users understand what they did wrong.
![Docusaurus navbar error message screenshot](./img/navbar-error.jpg)
### Translations
We made it possible to translate some new elements:
- [#8677](https://github.com/facebook/docusaurus/pull/8677) introduces a new `process.env.DOCUSAURUS_CURRENT_LOCALE` (experimental) allowing you to localize your config file, including site title, tagline, announcement bar, baseUrl...
- [#8616](https://github.com/facebook/docusaurus/pull/8616) allows to translate the navbar and footer logo alt text
We added default theme translation support for multiple languages:
- 🇭🇺 [#8668](https://github.com/facebook/docusaurus/pull/8668): Hungarian
- 🇳🇴 [#8631](https://github.com/facebook/docusaurus/pull/8631): Norwegian (Bokmål)
:::tip
Completing theme translations is an [ongoing effort](https://github.com/facebook/docusaurus/issues/3526) and an easy way to contribute to Docusaurus. We add new theme features regularly, for which we often [need new translations](https://github.com/facebook/docusaurus/issues/3526).
:::
## Other changes
Other notable changes include:
- [#8674](https://github.com/facebook/docusaurus/pull/8674): respect `prefers-reduced-motion: reduce` media query
- [#8712](https://github.com/facebook/docusaurus/pull/8712): use a navbar item of type `docSidebar` in template
- [#8801](https://github.com/facebook/docusaurus/pull/8801): allow tabs children to be falsy
- [#8757](https://github.com/facebook/docusaurus/pull/8757): make search page react to external query-string changes
- [#8803](https://github.com/facebook/docusaurus/pull/8803): fix code block buttons position in RTL
- [#8615](https://github.com/facebook/docusaurus/pull/8615): fix color mode toggle when using dark navbar
- [#8699](https://github.com/facebook/docusaurus/pull/8699): fix navbar dropdown tab focus bug
Check the **[2.4.0 changelog entry](/changelog/2.4.0)** for an exhaustive list of changes.

View file

@ -357,6 +357,7 @@ const config = {
}
return `https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`;
},
remarkPlugins: [npm2yarn],
postsPerPage: 5,
feedOptions: {
type: 'all',

View file

@ -0,0 +1,33 @@
/**
* 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.
*/
import React from 'react';
import BrowserWindow from './index';
// Quick and dirty component, to improve later if needed
export default function IframeWindow({url}: {url: string}): JSX.Element {
return (
<div style={{padding: 10}}>
<BrowserWindow
url={url}
style={{
minWidth: 'min(100%,45vw)',
width: 800,
maxWidth: '100%',
overflow: 'hidden',
}}
bodyStyle={{padding: 0}}>
<iframe
src={url}
title={url}
style={{display: 'block', width: '100%', height: 300}}
/>
</BrowserWindow>
</div>
);
}

View file

@ -51,3 +51,17 @@ export default function BrowserWindow({
</div>
);
}
// Quick and dirty component, to improve later if needed
export function IframeWindow({url}: {url: string}): JSX.Element {
return (
<div style={{padding: 10}}>
<BrowserWindow
url={url}
style={{minWidth: '40vw', maxWidth: 400}}
bodyStyle={{padding: 0}}>
<iframe src={url} title={url} style={{width: '100%', height: 300}} />
</BrowserWindow>
</div>
);
}

View file

@ -9,15 +9,24 @@ import React, {type ReactNode, useState} from 'react';
export default function ErrorBoundaryTestButton({
children = 'Boom!',
message = 'Boom!\nSomething bad happened, but you can try again!',
cause,
}: {
children?: ReactNode;
message?: string;
cause?: string;
}): JSX.Element {
const [state, setState] = useState(false);
if (state) {
throw new Error('Boom!\nSomething bad happened, but you can try again!');
throw new Error(message, {
cause: cause ? new Error(cause) : undefined,
});
}
return (
<button type="button" onClick={() => setState(true)}>
<button
className="button button--danger"
type="button"
onClick={() => setState(true)}>
{children}
</button>
);

View file

@ -0,0 +1,28 @@
---
description: How Docusaurus works to build your app
---
# Architecture
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Zoom from 'react-medium-image-zoom';
```
<Zoom>
![Architecture overview](/img/architecture.png)
</Zoom>
This diagram shows how Docusaurus works to build your app. Plugins each collect their content and emit JSON data; themes provide layout components which receive the JSON data as route modules. The bundler bundles all the components and emits a server bundle and a client bundle.
Although you (either plugin authors or site creators) are writing JavaScript all the time, bear in mind that the JS is actually run in different environments:
- All plugin lifecycle methods are run in Node. Therefore, until we support ES Modules in our codebase, plugin source code must be provided as CommonJS that can be `require`'d.
- The theme code is built with Webpack. They can be provided as ESM—following React conventions.
Plugin code and theme code never directly import each other: they only communicate through protocols (in our case, through JSON temp files and calls to `addRoute`). A useful mental model is to imagine that the plugins are not written in JavaScript, but in another language like Rust. The only way to interact with plugins for the user is through `docusaurus.config.js`, which itself is run in Node (hence you can use `require` and pass callbacks as plugin options).
During bundling, the config file itself is serialized and bundled, allowing the theme to access config options like `themeConfig` or `baseUrl` through [`useDocusaurusContext()`](../docusaurus-core.mdx#useDocusaurusContext). However, the `siteConfig` object only contains **serializable values** (values that are preserved after `JSON.stringify()`). Functions, regexes, etc. would be lost on the client side. The `themeConfig` is designed to be entirely serializable.

View file

@ -0,0 +1,186 @@
---
description: How the Docusaurus client is structured
---
# Client architecture
## Theme aliases {#theme-aliases}
A theme works by exporting a set of components, e.g. `Navbar`, `Layout`, `Footer`, to render the data passed down from plugins. Docusaurus and users use these components by importing them using the `@theme` webpack alias:
```js
import Navbar from '@theme/Navbar';
```
The alias `@theme` can refer to a few directories, in the following priority:
1. A user's `website/src/theme` directory, which is a special directory that has the higher precedence.
2. A Docusaurus theme package's `theme` directory.
3. Fallback components provided by Docusaurus core (usually not needed).
This is called a _layered architecture_: a higher-priority layer providing the component would shadow a lower-priority layer, making swizzling possible. Given the following structure:
```
website
├── node_modules
│ └── @docusaurus/theme-classic
│ └── theme
│ └── Navbar.js
└── src
└── theme
└── Navbar.js
```
`website/src/theme/Navbar.js` takes precedence whenever `@theme/Navbar` is imported. This behavior is called component swizzling. If you are familiar with Objective C where a function's implementation can be swapped during runtime, it's the exact same concept here with changing the target `@theme/Navbar` is pointing to!
We already talked about how the "userland theme" in `src/theme` can re-use a theme component through the [`@theme-original`](#wrapping) alias. One theme package can also wrap a component from another theme, by importing the component from the initial theme, using the `@theme-init` import.
Here's an example of using this feature to enhance the default theme `CodeBlock` component with a `react-live` playground feature.
```js
import InitialCodeBlock from '@theme-init/CodeBlock';
import React from 'react';
export default function CodeBlock(props) {
return props.live ? (
<ReactLivePlayground {...props} />
) : (
<InitialCodeBlock {...props} />
);
}
```
Check the code of `@docusaurus/theme-live-codeblock` for details.
:::caution
Unless you want to publish a re-usable "theme enhancer" (like `@docusaurus/theme-live-codeblock`), you likely don't need `@theme-init`.
:::
It can be quite hard to wrap your mind around these aliases. Let's imagine the following case with a super convoluted setup with three themes/plugins and the site itself all trying to define the same component. Internally, Docusaurus loads these themes as a "stack".
```text
+-------------------------------------------------+
| `website/src/theme/CodeBlock.js` | <-- `@theme/CodeBlock` always points to the top
+-------------------------------------------------+
| `theme-live-codeblock/theme/CodeBlock/index.js` | <-- `@theme-original/CodeBlock` points to the topmost non-swizzled component
+-------------------------------------------------+
| `plugin-awesome-codeblock/theme/CodeBlock.js` |
+-------------------------------------------------+
| `theme-classic/theme/CodeBlock/index.js` | <-- `@theme-init/CodeBlock` always points to the bottom
+-------------------------------------------------+
```
The components in this "stack" are pushed in the order of `preset plugins > preset themes > plugins > themes > site`, so the swizzled component in `website/src/theme` always comes out on top because it's loaded last.
`@theme/*` always points to the topmost component—when `CodeBlock` is swizzled, all other components requesting `@theme/CodeBlock` receive the swizzled version.
`@theme-original/*` always points to the topmost non-swizzled component. That's why you can import `@theme-original/CodeBlock` in the swizzled component—it points to the next one in the "component stack", a theme-provided one. Plugin authors should not try to use this because your component could be the topmost component and cause a self-import.
`@theme-init/*` always points to the bottommost component—usually, this comes from the theme or plugin that first provides this component. Individual plugins / themes trying to enhance code block can safely use `@theme-init/CodeBlock` to get its basic version. Site creators should generally not use this because you likely want to enhance the _topmost_ instead of the _bottommost_ component. It's also possible that the `@theme-init/CodeBlock` alias does not exist at all—Docusaurus only creates it when it points to a different one from `@theme-original/CodeBlock`, i.e. when it's provided by more than one theme. We don't waste aliases!
## Client modules {#client-modules}
Client modules are part of your site's bundle, just like theme components. However, they are usually side-effect-ful. Client modules are anything that can be `import`ed by Webpack—CSS, JS, etc. JS scripts usually work on the global context, like registering event listeners, creating global variables...
These modules are imported globally before React even renders the initial UI.
```js title="@docusaurus/core/App.tsx"
// How it works under the hood
import '@generated/client-modules';
```
Plugins and sites can both declare client modules, through [`getClientModules`](../api/plugin-methods/lifecycle-apis.mdx#getClientModules) and [`siteConfig.clientModules`](../api/docusaurus.config.js.mdx#clientModules), respectively.
Client modules are called during server-side rendering as well, so remember to check the [execution environment](./ssg.mdx#escape-hatches) before accessing client-side globals.
```js title="mySiteGlobalJs.js"
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
// As soon as the site loads in the browser, register a global event listener
window.addEventListener('keydown', (e) => {
if (e.code === 'Period') {
location.assign(location.href.replace('.com', '.dev'));
}
});
}
```
CSS stylesheets imported as client modules are [global](../styling-layout.mdx#global-styles).
```css title="mySiteGlobalCss.css"
/* This stylesheet is global. */
.globalSelector {
color: red;
}
```
### Client module lifecycles {#client-module-lifecycles}
Besides introducing side-effects, client modules can optionally export two lifecycle functions: `onRouteUpdate` and `onRouteDidUpdate`.
Because Docusaurus builds a single-page application, `script` tags will only be executed the first time the page loads, but will not re-execute on page transitions. These lifecycles are useful if you have some imperative JS logic that should execute every time a new page has loaded, e.g., to manipulate DOM elements, to send analytics data, etc.
For every route transition, there will be several important timings:
1. The user clicks a link, which causes the router to change its current location.
2. Docusaurus preloads the next route's assets, while keeping displaying the current page's content.
3. The next route's assets have loaded.
4. The new location's route component gets rendered to DOM.
`onRouteUpdate` will be called at event (2), and `onRouteDidUpdate` will be called at (4). They both receive the current location and the previous location (which can be `null`, if this is the first screen).
`onRouteUpdate` can optionally return a "cleanup" callback, which will be called at (3). For example, if you want to display a progress bar, you can start a timeout in `onRouteUpdate`, and clear the timeout in the callback. (The classic theme already provides an `nprogress` integration this way.)
Note that the new page's DOM is only available during event (4). If you need to manipulate the new page's DOM, you'll likely want to use `onRouteDidUpdate`, which will be fired as soon as the DOM on the new page has mounted.
```js title="myClientModule.js"
import type {Location} from 'history';
export function onRouteDidUpdate({location, previousLocation}) {
// Don't execute if we are still on the same page; the lifecycle may be fired
// because the hash changes (e.g. when navigating between headings)
if (location.pathname !== previousLocation?.pathname) {
const title = document.getElementsByTagName('h1')[0];
if (title) {
title.innerText += '❤️';
}
}
}
export function onRouteUpdate({location, previousLocation}) {
if (location.pathname !== previousLocation?.pathname) {
const progressBarTimeout = window.setTimeout(() => {
nprogress.start();
}, delay);
return () => window.clearTimeout(progressBarTimeout);
}
return undefined;
}
```
Or, if you are using TypeScript and you want to leverage contextual typing:
```ts title="myClientModule.ts"
import type {ClientModule} from '@docusaurus/types';
const module: ClientModule = {
onRouteUpdate({location, previousLocation}) {
// ...
},
onRouteDidUpdate({location, previousLocation}) {
// ...
},
};
export default module;
```
Both lifecycles will fire on first render, but they will not fire on server-side, so you can safely access browser globals in them.
:::tip Prefer using React
Client module lifecycles are purely imperative, and you can't use React hooks or access React contexts within them. If your operations are state-driven or involve complicated DOM manipulations, you should consider [swizzling components](../swizzling.mdx) instead.
:::

View file

@ -0,0 +1,11 @@
# Advanced Tutorials
This section is not going to be very structured, but we will cover the following topics:
```mdx-code-block
import DocCardList from '@theme/DocCardList';
<DocCardList />
```
We will assume that you have finished the guides, and know the basics like how to configure plugins, how to write React components, etc. These sections will have plugin authors and code contributors in mind, so we may occasionally refer to [plugin APIs](../api/plugin-methods/README.mdx) or other architecture details. Don't panic if you don't understand everything😉

View file

@ -0,0 +1,129 @@
# Plugins
Plugins are the building blocks of features in a Docusaurus 2 site. Each plugin handles its own individual feature. Plugins may work and be distributed as part of a bundle via presets.
## Creating plugins {#creating-plugins}
A plugin is a function that takes two parameters: `context` and `options`. It returns a plugin instance object (or a promise). You can create plugins as functions or modules. For more information, refer to the [plugin method references section](../api/plugin-methods/README.mdx).
### Function definition {#function-definition}
You can use a plugin as a function directly included in the Docusaurus config file:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
// highlight-start
async function myPlugin(context, options) {
// ...
return {
name: 'my-plugin',
async loadContent() {
// ...
},
async contentLoaded({content, actions}) {
// ...
},
/* other lifecycle API */
};
},
// highlight-end
],
};
```
### Module definition {#module-definition}
You can use a plugin as a module path referencing a separate file or npm package:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
// without options:
'./my-plugin',
// or with options:
['./my-plugin', options],
],
};
```
Then in the folder `my-plugin`, you can create an `index.js` such as this:
```js title="my-plugin/index.js"
module.exports = async function myPlugin(context, options) {
// ...
return {
name: 'my-plugin',
async loadContent() {
/* ... */
},
async contentLoaded({content, actions}) {
/* ... */
},
/* other lifecycle API */
};
};
```
---
You can view all plugins installed in your site using the [debug plugin's metadata panel](/__docusaurus/debug/metadata).
Plugins come as several types:
- `package`: an external package you installed
- `project`: a plugin you created in your project, given to Docusaurus as a local file path
- `local`: a plugin created using the function definition
- `synthetic`: a "fake plugin" Docusaurus created internally, so we take advantage of our modular architecture and don't let the core do much special work. You won't see this in the metadata because it's an implementation detail.
You can access them on the client side with `useDocusaurusContext().siteMetadata.pluginVersions`.
## Plugin design {#plugin-design}
Docusaurus' implementation of the plugins system provides us with a convenient way to hook into the website's lifecycle to modify what goes on during development/build, which involves (but is not limited to) extending the webpack config, modifying the data loaded, and creating new components to be used in a page.
### Theme design {#theme-design}
When plugins have loaded their content, the data is made available to the client side through actions like [`createData` + `addRoute`](../api/plugin-methods/lifecycle-apis.mdx#addRoute) or [`setGlobalData`](../api/plugin-methods/lifecycle-apis.mdx#setGlobalData). This data has to be _serialized_ to plain strings, because [plugins and themes run in different environments](./architecture.mdx). Once the data arrives on the client side, the rest becomes familiar to React developers: data is passed along components, components are bundled with Webpack, and rendered to the window through `ReactDOM.render`...
**Themes provide the set of UI components to render the content.** Most content plugins need to be paired with a theme in order to be actually useful. The UI is a separate layer from the data schema, which makes swapping designs easy.
For example, a Docusaurus blog may consist of a blog plugin and a blog theme.
:::note
This is a contrived example: in practice, `@docusaurus/theme-classic` provides the theme for docs, blog, and layouts.
:::
```js title="docusaurus.config.js"
module.exports = {
// highlight-next-line
themes: ['theme-blog'],
plugins: ['plugin-content-blog'],
};
```
And if you want to use Bootstrap styling, you can swap out the theme with `theme-blog-bootstrap` (another fictitious non-existing theme):
```js title="docusaurus.config.js"
module.exports = {
// highlight-next-line
themes: ['theme-blog-bootstrap'],
plugins: ['plugin-content-blog'],
};
```
Now, although the theme receives the same data from the plugin, how the theme chooses to _render_ the data as UI can be drastically different.
While themes share the exact same lifecycle methods with plugins, themes' implementations can look very different from those of plugins based on themes' designed objectives.
Themes are designed to complete the build of your Docusaurus site and supply the components used by your site, plugins, and the themes themselves. A theme still acts like a plugin and exposes some lifecycle methods, but most likely they would not use [`loadContent`](../api/plugin-methods/lifecycle-apis.mdx#loadContent), since they only receive data from plugins, but don't generate data themselves; themes are typically also accompanied by an `src/theme` directory full of components, which are made known to the core through the [`getThemePath`](../api/plugin-methods/extend-infrastructure.mdx#getThemePath) lifecycle.
To summarize:
- Themes share the same lifecycle methods with Plugins
- Themes are run after all existing Plugins
- Themes add component aliases by providing `getThemePath`.

View file

@ -0,0 +1,296 @@
---
description: "Docusaurus' routing system follows single-page application conventions: one route, one component."
---
# Routing
```mdx-code-block
import Link from '@docusaurus/Link';
import {useLatestVersion, useActiveDocContext} from '@docusaurus/plugin-content-docs/client';
import {useLocation} from '@docusaurus/router';
import BrowserWindow from '@site/src/components/BrowserWindow';
```
Docusaurus' routing system follows single-page application conventions: one route, one component. In this section, we will begin by talking about routing within the three content plugins (docs, blog, and pages), and then go beyond to talk about the underlying routing system.
## Routing in content plugins {#routing-in-content-plugins}
Every content plugin provides a `routeBasePath` option. It defines where the plugins append their routes to. By default, the docs plugin puts its routes under `/docs`; the blog plugin, `/blog`; and the pages plugin, `/`. You can think about the route structure like this:
```mermaid
graph LR;
A(["https://example.com/"])
B(["/base-url/"])
C(["/docs/"])
D(["/blog/"])
E(["/"])
F["All docs <br/>routes"]
G["All blog <br/>routes"]
H["All pages <br/>routes"]
A---B;
B---C;
B---D;
B---E;
C---F;
D---G;
E---H;
```
Any route will be matched against this nested route config until a good match is found. For example, when given a route `/docs/configuration`, Docusaurus first enters the `/docs` branch, and then searches among the subroutes created by the docs plugin.
Changing `routeBasePath` can effectively alter your site's route structure. For example, in [Docs-only mode](../guides/docs/docs-introduction.mdx#docs-only-mode), we mentioned that configuring `routeBasePath: '/'` for docs means that all routes that the docs plugin create would not have the `/docs` prefix, yet it doesn't prevent you from having more subroutes like `/blog` created by other plugins.
Next, let's look at how the three plugins structure their own "boxes of subroutes".
### Pages routing {#pages-routing}
Pages routing are straightforward: the file paths directly map to URLs, without any other way to customize. See the [pages docs](../guides/creating-pages.mdx#routing) for more information.
The component used for Markdown pages is `@theme/MDXPage`. React pages are directly used as the route's component.
### Blog routing {#blog-routing}
The blog creates the following routes:
- **Posts list pages**: `/`, `/page/2`, `/page/3`...
- The component is `@theme/BlogListPage`.
- **Post pages**: `/2021/11/21/algolia-docsearch-migration`, `/2021/05/12/announcing-docusaurus-two-beta`...
- Generated from each Markdown post.
- The routes are fully customizable through the `slug` front matter.
- The component is `@theme/BlogPostPage`.
- **Tags list page**: `/tags`
- The route is customizable through the `tagsBasePath` option.
- The component is `@theme/BlogTagsListPage`.
- **Tag pages**: `/tags/adoption`, `/tags/beta`...
- Generated through the tags defined in each post's front matter.
- The routes always have base defined in `tagsBasePath`, but the subroutes are customizable through the tag's `permalink` field.
- The component is `@theme/BlogTagsPostsPage`.
- **Archive page**: `/archive`
- The route is customizable through the `archiveBasePath` option.
- The component is `@theme/BlogArchivePage`.
### Docs routing {#docs-routing}
The docs is the only plugin that creates **nested routes**. At the top, it registers [**version paths**](../guides/docs/versioning.mdx): `/`, `/next`, `/2.0.0-beta.13`... which provide the version context, including the layout and sidebar. This ensures that when switching between individual docs, the sidebar's state is preserved, and that you can switch between versions through the navbar dropdown while staying on the same doc. The component used is `@theme/DocPage`.
```mdx-code-block
export const URLPath = () => <code>{useLocation().pathname}</code>;
export const FilePath = () => {
const currentVersion = useActiveDocContext('default').activeVersion.name;
return <code>{currentVersion === 'current' ? './docs/' : `./versioned_docs/version-${currentVersion}/`}advanced/routing.md</code>;
}
```
The individual docs are rendered in the remaining space after the navbar, footer, sidebar, etc. have all been provided by the `DocPage` component. For example, this page, <URLPath />, is generated from the file at <FilePath />. The component used is `@theme/DocItem`.
The doc's `slug` front matter customizes the last part of the route, but the base route is always defined by the plugin's `routeBasePath` and the version's `path`.
### File paths and URL paths {#file-paths-and-url-paths}
Throughout the documentation, we always try to be unambiguous about whether we are talking about file paths or URL paths. Content plugins usually map file paths directly to URL paths, for example, `./docs/advanced/routing.md` will become `/docs/advanced/routing`. However, with `slug`, you can make URLs totally decoupled from the file structure.
When writing links in Markdown, you could either mean a _file path_, or a _URL path_, which Docusaurus would use several heuristics to determine.
- If the path has a `@site` prefix, it is _always_ an asset file path.
- If the path has an `http(s)://` prefix, it is _always_ a URL path.
- If the path doesn't have an extension, it is a URL path. For example, a link `[page](../plugins)` on a page with URL `/docs/advanced/routing` will link to `/docs/plugins`. Docusaurus will only detect broken links when building your site (when it knows the full route structure), but will make no assumptions about the existence of a file. It is exactly equivalent to writing `<a href="../plugins">page</a>` in a JSX file.
- If the path has an `.md(x)` extension, Docusaurus would try to resolve that Markdown file to a URL, and replace the file path with a URL path.
- If the path has any other extension, Docusaurus would treat it as [an asset](../guides/markdown-features/markdown-features-assets.mdx) and bundle it.
The following directory structure may help you visualize this file → URL mapping. Assume that there's no slug customization in any page.
<details>
<summary>A sample site structure</summary>
```bash
.
├── blog # blog plugin has routeBasePath: '/blog'
│ ├── 2019-05-28-first-blog-post.md # -> /blog/2019/05/28/first-blog-post
│ ├── 2019-05-29-long-blog-post.md # -> /blog/2019/05/29/long-blog-post
│ ├── 2021-08-01-mdx-blog-post.mdx # -> /blog/2021/08/01/mdx-blog-post
│ └── 2021-08-26-welcome
│ ├── docusaurus-plushie-banner.jpeg
│ └── index.md # -> /blog/2021/08/26/welcome
├── docs # docs plugin has routeBasePath: '/docs'; current version has base path '/'
│ ├── intro.md # -> /docs/intro
│ ├── tutorial-basics
│ │ ├── _category_.json
│ │ ├── congratulations.md # -> /docs/tutorial-basics/congratulations
│ │ └── markdown-features.mdx # -> /docs/tutorial-basics/congratulations
│ └── tutorial-extras
│ ├── _category_.json
│ ├── manage-docs-versions.md # -> /docs/tutorial-extras/manage-docs-versions
│ └── translate-your-site.md # -> /docs/tutorial-extras/translate-your-site
├── src
│ └── pages # pages plugin has routeBasePath: '/'
│ ├── index.module.css
│ ├── index.tsx # -> /
│ └── markdown-page.md # -> /markdown-page
└── versioned_docs
└── version-1.0.0 # version has base path '/1.0.0'
├── intro.md # -> /docs/1.0.0/intro
├── tutorial-basics
│ ├── _category_.json
│ ├── congratulations.md # -> /docs/1.0.0/tutorial-basics/congratulations
│ └── markdown-features.mdx # -> /docs/1.0.0/tutorial-basics/congratulations
└── tutorial-extras
├── _category_.json
├── manage-docs-versions.md # -> /docs/1.0.0/tutorial-extras/manage-docs-versions
└── translate-your-site.md # -> /docs/1.0.0/tutorial-extras/translate-your-site
```
</details>
So much about content plugins. Let's take one step back and talk about how routing works in a Docusaurus app in general.
## Routes become HTML files {#routes-become-html-files}
Because Docusaurus is a server-side rendering framework, all routes generated will be server-side rendered into static HTML files. If you are familiar with the behavior of HTTP servers like [Apache2](https://httpd.apache.org/docs/trunk/getting-started.html), you will understand how this is done: when the browser sends a request to the route `/docs/advanced/routing`, the server interprets that as request for the HTML file `/docs/advanced/routing/index.html`, and returns that.
The `/docs/advanced/routing` route can correspond to either `/docs/advanced/routing/index.html` or `/docs/advanced/routing.html`. Some hosting providers differentiate between them using the presence of a trailing slash, and may or may not tolerate the other. Read more in the [trailing slash guide](https://github.com/slorber/trailing-slash-guide).
For example, the build output of the directory above is (ignoring other assets and JS bundle):
<details>
<summary>Output of the above workspace</summary>
```bash
build
├── 404.html # /404/
├── blog
│ ├── archive
│ │ └── index.html # /blog/archive/
│ ├── first-blog-post
│ │ └── index.html # /blog/first-blog-post/
│ ├── index.html # /blog/
│ ├── long-blog-post
│ │ └── index.html # /blog/long-blog-post/
│ ├── mdx-blog-post
│ │ └── index.html # /blog/mdx-blog-post/
│ ├── tags
│ │ ├── docusaurus
│ │ │ └── index.html # /blog/tags/docusaurus/
│ │ ├── hola
│ │ │ └── index.html # /blog/tags/hola/
│ │ └── index.html # /blog/tags/
│ └── welcome
│ └── index.html # /blog/welcome/
├── docs
│ ├── 1.0.0
│ │ ├── intro
│ │ │ └── index.html # /docs/1.0.0/intro/
│ │ ├── tutorial-basics
│ │ │ ├── congratulations
│ │ │ │ └── index.html # /docs/1.0.0/tutorial-basics/congratulations/
│ │ │ └── markdown-features
│ │ │ └── index.html # /docs/1.0.0/tutorial-basics/markdown-features/
│ │ └── tutorial-extras
│ │ ├── manage-docs-versions
│ │ │ └── index.html # /docs/1.0.0/tutorial-extras/manage-docs-versions/
│ │ └── translate-your-site
│ │ └── index.html # /docs/1.0.0/tutorial-extras/translate-your-site/
│ ├── intro
│ │ └── index.html # /docs/1.0.0/intro/
│ ├── tutorial-basics
│ │ ├── congratulations
│ │ │ └── index.html # /docs/tutorial-basics/congratulations/
│ │ └── markdown-features
│ │ └── index.html # /docs/tutorial-basics/markdown-features/
│ └── tutorial-extras
│ ├── manage-docs-versions
│ │ └── index.html # /docs/tutorial-extras/manage-docs-versions/
│ └── translate-your-site
│ └── index.html # /docs/tutorial-extras/translate-your-site/
├── index.html # /
└── markdown-page
└── index.html # /markdown-page/
```
</details>
If `trailingSlash` is set to `false`, the build would emit `intro.html` instead of `intro/index.html`.
All HTML files will reference its JS assets using absolute URLs, so in order for the correct assets to be located, you have to configure the `baseUrl` field. Note that `baseUrl` doesn't affect the emitted bundle's file structure: the base URL is one level above the Docusaurus routing system. You can see the aggregate of `url` and `baseUrl` as the actual location of your Docusaurus site.
For example, the emitted HTML would contain links like `<link rel="preload" href="/assets/js/runtime~main.7ed5108a.js" as="script">`. Because absolute URLs are resolved from the host, if the bundle placed under the path `https://example.com/base/`, the link will point to `https://example.com/assets/js/runtime~main.7ed5108a.js`, which is, well, non-existent. By specifying `/base/` as base URL, the link will correctly point to `/base/assets/js/runtime~main.7ed5108a.js`.
Localized sites have the locale as part of the base URL as well. For example, `https://docusaurus.io/zh-CN/docs/advanced/routing/` has base URL `/zh-CN/`.
## Generating and accessing routes {#generating-and-accessing-routes}
The `addRoute` lifecycle action is used to generate routes. It registers a piece of route config to the route tree, giving a route, a component, and props that the component needs. The props and the component are both provided as paths for the bundler to `require`, because as explained in the [architecture overview](architecture.mdx), server and client only communicate through temp files.
All routes are aggregated in `.docusaurus/routes.js`, which you can view with the debug plugin's [routes panel](/__docusaurus/debug/routes).
On the client side, we offer `@docusaurus/router` to access the page's route. `@docusaurus/router` is a re-export of the [`react-router-dom`](https://www.npmjs.com/package/react-router-dom/v/5.3.0) package. For example, you can use `useLocation` to get the current page's [location](https://developer.mozilla.org/en-US/docs/Web/API/Location), and `useHistory` to access the [history object](https://developer.mozilla.org/en-US/docs/Web/API/History). (They are not the same as the browser API, although similar in functionality. Refer to the React Router documentation for specific APIs.)
This API is **SSR safe**, as opposed to the browser-only `window.location`.
```jsx title="myComponent.js"
import React from 'react';
import {useLocation} from '@docusaurus/router';
export function PageRoute() {
// React router provides the current component's route, even in SSR
const location = useLocation();
return (
<span>
We are currently on <code>{location.pathname}</code>
</span>
);
}
```
```mdx-code-block
export function PageRoute() {
const location = useLocation();
return (
<span>
We are currently on <code>{location.pathname}</code>
</span>
);
}
<BrowserWindow>
<PageRoute />
</BrowserWindow>
```
## Escaping from SPA redirects {#escaping-from-spa-redirects}
Docusaurus builds a [single-page application](https://developer.mozilla.org/en-US/docs/Glossary/SPA), where route transitions are done through the `history.push()` method of React router. This operation is done on the client side. However, the prerequisite for a route transition to happen this way is that the target URL is known to our router. Otherwise, the router catches this path and displays a 404 page instead.
If you put some HTML pages under the `static` folder, they will be copied to the build output and therefore become accessible as part of your website, yet it's not part of the Docusaurus route system. We provide a `pathname://` protocol that allows you to redirect to another part of your domain in a non-SPA fashion, as if this route is an external link. Try the following two links:
```md
- [/pure-html](/pure-html)
- [pathname:///pure-html](pathname:///pure-html)
```
<BrowserWindow>
- [/pure-html](/pure-html)
- [pathname:///pure-html](pathname:///pure-html)
</BrowserWindow>
:::tip
The first link will **not** trigger a "broken links detected" check during the production build, because the respective file actually exists. Nevertheless, when you click on the link, a "page not found" will be displayed until you refresh.
:::
The `pathname://` protocol is useful for referencing any content in the static folder. For example, Docusaurus would convert [all Markdown static assets to require() calls](../guides/markdown-features/markdown-features-assets.mdx#static-assets). You can use `pathname://` to keep it a regular link instead of being hashed by Webpack.
```md title="my-doc.md"
![An image from the static](pathname:///img/docusaurus.png)
[An asset from the static](pathname:///files/asset.pdf)
```
Docusaurus will only strip the `pathname://` prefix without processing the content.

View file

@ -0,0 +1,218 @@
---
sidebar_label: Static site generation
description: Docusaurus statically renders your React code into HTML, allowing faster load speed and better SEO.
---
# Static site generation (SSG)
In [architecture](architecture.mdx), we mentioned that the theme is run in Webpack. But beware: that doesn't mean it always has access to browser globals! The theme is built twice:
- During **server-side rendering**, the theme is compiled in a sandbox called [React DOM Server](https://reactjs.org/docs/react-dom-server.html). You can see this as a "headless browser", where there is no `window` or `document`, only React. SSR produces static HTML pages.
- During **client-side rendering**, the theme is compiled to JavaScript that gets eventually executed in the browser, so it has access to browser variables.
:::info SSR or SSG?
_Server-side rendering_ and _static site generation_ can be different concepts, but we use them interchangeably.
Strictly speaking, Docusaurus is a static site generator, because there's no server-side runtime—we statically render to HTML files that are deployed on a CDN, instead of dynamically pre-rendering on each request. This differs from the working model of [Next.js](https://nextjs.org/).
:::
Therefore, while you probably know not to access Node globals like `process` ([or can we?](#node-env)) or the `'fs'` module, you can't freely access browser globals either.
```jsx
import React from 'react';
export default function WhereAmI() {
return <span>{window.location.href}</span>;
}
```
This looks like idiomatic React, but if you run `docusaurus build`, you will get an error:
```
ReferenceError: window is not defined
```
This is because during server-side rendering, the Docusaurus app isn't actually run in browser, and it doesn't know what `window` is.
```mdx-code-block
<details id="node-env">
<summary>What about <code>process.env.NODE_ENV</code>?</summary>
```
One exception to the "no Node globals" rule is `process.env.NODE_ENV`. In fact, you can use it in React, because Webpack injects this variable as a global:
```jsx
import React from 'react';
export default function expensiveComp() {
if (process.env.NODE_ENV === 'development') {
return <>This component is not shown in development</>;
}
const res = someExpensiveOperationThatLastsALongTime();
return <>{res}</>;
}
```
During Webpack build, the `process.env.NODE_ENV` will be replaced with the value, either `'development'` or `'production'`. You will then get different build results after dead code elimination:
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```mdx-code-block
<Tabs>
<TabItem value="Development">
```
```diff
import React from 'react';
export default function expensiveComp() {
// highlight-next-line
if ('development' === 'development') {
+ return <>This component is not shown in development</>;
}
- const res = someExpensiveOperationThatLastsALongTime();
- return <>{res}</>;
}
```
```mdx-code-block
</TabItem>
<TabItem value="Production">
```
```diff
import React from 'react';
export default function expensiveComp() {
// highlight-next-line
- if ('production' === 'development') {
- return <>This component is not shown in development</>;
- }
+ const res = someExpensiveOperationThatLastsALongTime();
+ return <>{res}</>;
}
```
```mdx-code-block
</TabItem>
</Tabs>
</details>
```
## Understanding SSR {#understanding-ssr}
React is not just a dynamic UI runtime—it's also a templating engine. Because Docusaurus sites mostly contain static contents, it should be able to work without any JavaScript (which React runs in), but only plain HTML/CSS. And that's what server-side rendering offers: statically rendering your React code into HTML, without any dynamic content. An HTML file has no concept of client state (it's purely markup), hence it shouldn't rely on browser APIs.
These HTML files are the first to arrive at the user's browser screen when a URL is visited (see [routing](routing.mdx)). Afterwards, the browser fetches and runs other JS code to provide the "dynamic" parts of your site—anything implemented with JavaScript. However, before that, the main content of your page is already visible, allowing faster loading.
In CSR-only apps, all DOM elements are generated on client side with React, and the HTML file only ever contains one root element for React to mount DOM to; in SSR, React is already facing a fully built HTML page, and it only needs to correlate the DOM elements with the virtual DOM in its model. This step is called "hydration". After React has hydrated the static markup, the app starts to work as any normal React app.
Note that Docusaurus is ultimately a single-page application, so static site generation is only an optimization (_progressive enhancement_, as it's called), but our functionality does not fully depend on those HTML files. This is contrary to site generators like [Jekyll](https://jekyllrb.com/) and [Docusaurus v1](https://v1.docusaurus.io/), where all files are statically transformed to markup, and interactiveness is added through external JavaScript linked with `<script>` tags. If you inspect the build output, you will still see JS assets under `build/assets/js`, which are, really, the core of Docusaurus.
## Escape hatches {#escape-hatches}
If you want to render any dynamic content on your screen that relies on the browser API to be functional at all, for example:
- Our [live codeblock](../guides/markdown-features/markdown-features-code-blocks.mdx#interactive-code-editor), which runs in the browser's JS runtime
- Our [themed image](../guides/markdown-features/markdown-features-assets.mdx#themed-images) that detects the user's color scheme to display different images
- The JSON viewer of our debug panel which uses the `window` global for styling
You may need to escape from SSR since static HTML can't display anything useful without knowing the client state.
:::caution
It is important for the first client-side render to produce the exact same DOM structure as server-side rendering, otherwise, React will correlate virtual DOM with the wrong DOM elements.
Therefore, the naïve attempt of `if (typeof window !== 'undefined) {/* render something */}` won't work appropriately as a browser vs. server detection, because the first client render would instantly render different markup from the server-generated one.
You can read more about this pitfall in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/).
:::
We provide several more reliable ways to escape SSR.
### `<BrowserOnly>` {#browseronly}
If you need to render some component in browser only (for example, because the component relies on browser specifics to be functional at all), one common approach is to wrap your component with [`<BrowserOnly>`](../docusaurus-core.mdx#browseronly) to make sure it's invisible during SSR and only rendered in CSR.
```jsx
import BrowserOnly from '@docusaurus/BrowserOnly';
function MyComponent(props) {
return (
// highlight-start
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const LibComponent =
require('some-lib-that-accesses-window').LibComponent;
return <LibComponent {...props} />;
}}
</BrowserOnly>
// highlight-end
);
}
```
It's important to realize that the children of `<BrowserOnly>` is not a JSX element, but a function that _returns_ an element. This is a design decision. Consider this code:
```jsx
import BrowserOnly from '@docusaurus/BrowserOnly';
function MyComponent() {
return (
<BrowserOnly>
{/* highlight-start */}
{/* DON'T DO THIS - doesn't actually work */}
<span>page url = {window.location.href}</span>
{/* highlight-end */}
</BrowserOnly>
);
}
```
While you may expect that `BrowserOnly` hides away the children during server-side rendering, it actually can't. When the React renderer tries to render this JSX tree, it does see the `{window.location.href}` variable as a node of this tree and tries to render it, although it's actually not used! Using a function ensures that we only let the renderer see the browser-only component when it's needed.
### `useIsBrowser` {#useisbrowser}
You can also use the `useIsBrowser()` hook to test if the component is currently in a browser environment. It returns `false` in SSR and `true` is CSR, after first client render. Use this hook if you only need to perform certain conditional operations on client-side, but not render an entirely different UI.
```jsx
import useIsBrowser from '@docusaurus/useIsBrowser';
function MyComponent() {
const isBrowser = useIsBrowser();
const location = isBrowser ? window.location.href : 'fetching location...';
return <span>{location}</span>;
}
```
### `useEffect` {#useeffect}
Lastly, you can put your logic in `useEffect()` to delay its execution until after first CSR. This is most appropriate if you are only performing side-effects but don't _get_ data from the client state.
```jsx
function MyComponent() {
useEffect(() => {
// Only logged in the browser console; nothing is logged during server-side rendering
console.log("I'm now in the browser");
}, []);
return <span>Some content...</span>;
}
```
### `ExecutionEnvironment` {#executionenvironment}
The [`ExecutionEnvironment`](../docusaurus-core.mdx#executionenvironment) namespace contains several values, and `canUseDOM` is an effective way to detect browser environment.
Beware that it essentially checked `typeof window !== 'undefined'` under the hood, so you should not use it for rendering-related logic, but only imperative code, like reacting to user input by sending web requests, or dynamically importing libraries, where DOM isn't updated at all.
```js title="a-client-module.js"
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
document.title = "I'm loaded!";
}
```

View file

@ -0,0 +1,598 @@
---
sidebar_position: 0
description: API reference for Docusaurus configuration file.
slug: /api/docusaurus-config
---
# `docusaurus.config.js`
## Overview {#overview}
`docusaurus.config.js` contains configurations for your site and is placed in the root directory of your site.
It usually exports a site configuration object:
```js title="docusaurus.config.js"
module.exports = {
// site config...
};
```
<details>
<summary>Config files also support config creator functions and async code.</summary>
```js title="docusaurus.config.js"
module.exports = function configCreator() {
return {
// site config...
};
};
```
```js title="docusaurus.config.js"
module.exports = async function configCreatorAsync() {
return {
// site config...
};
};
```
```js title="docusaurus.config.js"
module.exports = Promise.resolve({
// site config...
});
```
</details>
## Required fields {#required-fields}
### `title` {#title}
- Type: `string`
Title for your website. Will be used in metadata and as browser tab title.
```js title="docusaurus.config.js"
module.exports = {
title: 'Docusaurus',
};
```
### `url` {#url}
- Type: `string`
URL for your website. This can also be considered the top-level hostname. For example, `https://facebook.github.io` is the URL of https://facebook.github.io/metro/, and `https://docusaurus.io` is the URL for https://docusaurus.io. This field is related to the [`baseUrl`](#baseUrl) field.
```js title="docusaurus.config.js"
module.exports = {
url: 'https://docusaurus.io',
};
```
### `baseUrl` {#baseUrl}
- Type: `string`
Base URL for your site. Can be considered as the path after the host. For example, `/metro/` is the base URL of https://facebook.github.io/metro/. For URLs that have no path, the baseUrl should be set to `/`. This field is related to the [`url`](#url) field. Always has both leading and trailing slash.
```js title="docusaurus.config.js"
module.exports = {
baseUrl: '/',
};
```
## Optional fields {#optional-fields}
### `favicon` {#favicon}
- Type: `string | undefined`
Path to your site favicon; must be a URL that can be used in link's href. For example, if your favicon is in `static/img/favicon.ico`:
```js title="docusaurus.config.js"
module.exports = {
favicon: '/img/favicon.ico',
};
```
### `trailingSlash` {#trailingSlash}
- Type: `boolean | undefined`
Allow to customize the presence/absence of a trailing slash at the end of URLs/links, and how static HTML files are generated:
- `undefined` (default): keeps URLs untouched, and emit `/docs/myDoc/index.html` for `/docs/myDoc.md`
- `true`: add trailing slashes to URLs/links, and emit `/docs/myDoc/index.html` for `/docs/myDoc.md`
- `false`: remove trailing slashes from URLs/links, and emit `/docs/myDoc.html` for `/docs/myDoc.md`
:::tip
Each static hosting provider serves static files differently (this behavior may even change over time).
Refer to the [deployment guide](../deployment.mdx) and [slorber/trailing-slash-guide](https://github.com/slorber/trailing-slash-guide) to choose the appropriate setting.
:::
### `i18n` {#i18n}
- Type: `Object`
The i18n configuration object to [localize your site](../i18n/i18n-introduction.mdx).
Example:
<!-- cSpell:ignore فارسی -->
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fa'],
path: 'i18n',
localeConfigs: {
en: {
label: 'English',
direction: 'ltr',
htmlLang: 'en-US',
calendar: 'gregory',
path: 'en',
},
fa: {
label: 'فارسی',
direction: 'rtl',
htmlLang: 'fa-IR',
calendar: 'persian',
path: 'fa',
},
},
},
};
```
- `defaultLocale`: The locale that (1) does not have its name in the base URL (2) gets started with `docusaurus start` without `--locale` option (3) will be used for the `<link hrefLang="x-default">` tag
- `locales`: List of locales deployed on your site. Must contain `defaultLocale`.
- `path`: Root folder which all locale folders are relative to. Can be absolute or relative to the config file. Defaults to `i18n`.
- `localeConfigs`: Individual options for each locale.
- `label`: The label displayed for this locale in the locales dropdown.
- `direction`: `ltr` (default) or `rtl` (for [right-to-left languages](https://developer.mozilla.org/en-US/docs/Glossary/rtl) like Farsi, Arabic, Hebrew, etc.). Used to select the locale's CSS and HTML meta attribute.
- `htmlLang`: BCP 47 language tag to use in `<html lang="...">` (or any other DOM tag name) and in `<link ... hreflang="...">`
- `calendar`: the [calendar](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar) used to calculate the date era. Note that it doesn't control the actual string displayed: `MM/DD/YYYY` and `DD/MM/YYYY` are both `gregory`. To choose the format (`DD/MM/YYYY` or `MM/DD/YYYY`), set your locale name to `en-GB` or `en-US` (`en` means `en-US`).
- `path`: Root folder that all plugin localization folders of this locale are relative to. Will be resolved against `i18n.path`. Defaults to the locale's name. Note: this has no effect on the locale's `baseUrl`—customization of base URL is a work-in-progress.
### `noIndex` {#noIndex}
- Type: `boolean`
This option adds `<meta name="robots" content="noindex, nofollow">` to every page to tell search engines to avoid indexing your site (more information [here](https://moz.com/learn/seo/robots-meta-directives)).
Example:
```js title="docusaurus.config.js"
module.exports = {
noIndex: true, // Defaults to `false`
};
```
### `onBrokenLinks` {#onBrokenLinks}
- Type: `'ignore' | 'log' | 'warn' | 'throw'`
The behavior of Docusaurus when it detects any broken link.
By default, it throws an error, to ensure you never ship any broken link, but you can lower this security if needed.
:::note
The broken links detection is only available for a production build (`docusaurus build`).
:::
### `onBrokenMarkdownLinks` {#onBrokenMarkdownLinks}
- Type: `'ignore' | 'log' | 'warn' | 'throw'`
The behavior of Docusaurus when it detects any broken Markdown link.
By default, it prints a warning, to let you know about your broken Markdown link, but you can change this security if needed.
### `onDuplicateRoutes` {#onDuplicateRoutes}
- Type: `'ignore' | 'log' | 'warn' | 'throw'`
The behavior of Docusaurus when it detects any [duplicate routes](/guides/creating-pages.mdx#duplicate-routes).
By default, it displays a warning after you run `yarn start` or `yarn build`.
### `tagline` {#tagline}
- Type: `string`
The tagline for your website.
```js title="docusaurus.config.js"
module.exports = {
tagline:
'Docusaurus makes it easy to maintain Open Source documentation websites.',
};
```
### `organizationName` {#organizationName}
- Type: `string`
The GitHub user or organization that owns the repository. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js"
module.exports = {
// Docusaurus' organization is facebook
organizationName: 'facebook',
};
```
### `projectName` {#projectName}
- Type: `string`
The name of the GitHub repository. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js"
module.exports = {
projectName: 'docusaurus',
};
```
### `deploymentBranch` {#deploymentBranch}
- Type: `string`
The name of the branch to deploy the static files to. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js"
module.exports = {
deploymentBranch: 'gh-pages',
};
```
### `githubHost` {#githubHost}
- Type: `string`
The hostname of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js"
module.exports = {
githubHost: 'github.com',
};
```
### `githubPort` {#githubPort}
- Type: `string`
The port of your server. Useful if you are using GitHub Enterprise. You don't need this if you are not using the `docusaurus deploy` command.
```js title="docusaurus.config.js"
module.exports = {
githubPort: '22',
};
```
### `themeConfig` {#themeConfig}
- Type: `Object`
The [theme configuration](./themes/theme-configuration.mdx) object to customize your site UI like navbar and footer.
Example:
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
docs: {
sidebar: {
hideable: false,
autoCollapseCategories: false,
},
},
colorMode: {
defaultMode: 'light',
disableSwitch: false,
respectPrefersColorScheme: true,
},
navbar: {
title: 'Site Title',
logo: {
alt: 'Site Logo',
src: 'img/logo.svg',
width: 32,
height: 32,
},
items: [
{
to: 'docs/docusaurus.config.js',
activeBasePath: 'docs',
label: 'docusaurus.config.js',
position: 'left',
},
// ... other links
],
},
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [
{
label: 'Docs',
to: 'docs/doc1',
},
],
},
// ... other links
],
logo: {
alt: 'Meta Open Source Logo',
src: 'img/meta_oss_logo.png',
href: 'https://opensource.fb.com',
width: 160,
height: 51,
},
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`, // You can also put own HTML here
},
},
};
```
### `plugins` {#plugins}
- Type: `PluginConfig[]`
```ts
type PluginConfig = string | [string, any] | PluginModule | [PluginModule, any];
```
See [plugin method references](./plugin-methods/README.mdx) for the shape of a `PluginModule`.
```js title="docusaurus.config.js"
module.exports = {
plugins: [
'docusaurus-plugin-awesome',
['docusuarus-plugin-confetti', {fancy: false}],
() => ({
postBuild() {
console.log('Build finished');
},
}),
],
};
```
### `themes` {#themes}
- Type: `PluginConfig[]`
```js title="docusaurus.config.js"
module.exports = {
themes: ['@docusaurus/theme-classic'],
};
```
### `presets` {#presets}
- Type: `PresetConfig[]`
```ts
type PresetConfig = string | [string, any];
```
```js title="docusaurus.config.js"
module.exports = {
presets: [],
};
```
### `customFields` {#customfields}
Docusaurus guards `docusaurus.config.js` from unknown fields. To add a custom field, define it on `customFields`.
- Type: `Object`
```js title="docusaurus.config.js"
module.exports = {
customFields: {
admin: 'endi',
superman: 'lol',
},
};
```
Attempting to add unknown fields in the config will lead to errors during build time:
```bash
Error: The field(s) 'foo', 'bar' are not recognized in docusaurus.config.js
```
### `staticDirectories` {#staticDirectories}
An array of paths, relative to the site's directory or absolute. Files under these paths will be copied to the build output as-is.
- Type: `string[]`
Example:
```js title="docusaurus.config.js"
module.exports = {
staticDirectories: ['static'],
};
```
### `headTags` {#headTags}
An array of tags that will be inserted in the HTML `<head>`. The values must be objects that contain two properties; `tagName` and `attributes`. `tagName` must be a string that determines the tag being created; eg `"link"`. `attributes` must be an attribute-value map.
- Type: `{ tagName: string; attributes: Object; }[]`
Example:
```js title="docusaurus.config.js"
module.exports = {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'icon',
href: '/img/docusaurus.png',
},
},
],
};
```
This would become `<link rel="icon" href="img/docusaurus.png" />` in the generated HTML.
### `scripts` {#scripts}
An array of scripts to load. The values can be either strings or plain objects of attribute-value maps. The `<script>` tags will be inserted in the HTML `<head>`. If you use a plain object, the only required attribute is `src`, and any other attributes are permitted (each one should have boolean/string values).
Note that `<script>` added here are render-blocking, so you might want to add `async: true`/`defer: true` to the objects.
- Type: `(string | Object)[]`
Example:
```js title="docusaurus.config.js"
module.exports = {
scripts: [
// String format.
'https://docusaurus.io/script.js',
// Object format.
{
src: 'https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.0/clipboard.min.js',
async: true,
},
],
};
```
### `stylesheets` {#stylesheets}
An array of CSS sources to load. The values can be either strings or plain objects of attribute-value maps. The `<link>` tags will be inserted in the HTML `<head>`. If you use an object, the only required attribute is `href`, and any other attributes are permitted (each one should have boolean/string values).
- Type: `(string | Object)[]`
Example:
```js title="docusaurus.config.js"
module.exports = {
stylesheets: [
// String format.
'https://docusaurus.io/style.css',
// Object format.
{
href: 'http://mydomain.com/style.css',
},
],
};
```
:::info
By default, the `<link>` tags will have `rel="stylesheet"`, but you can explicitly add a custom `rel` value to inject any kind of `<link>` tag, not necessarily stylesheets.
:::
### `clientModules` {#clientModules}
An array of [client modules](../advanced/client.mdx#client-modules) to load globally on your site.
Example:
```js title="docusaurus.config.js"
module.exports = {
clientModules: [
require.resolve('./mySiteGlobalJs.js'),
require.resolve('./mySiteGlobalCss.css'),
],
};
```
### `ssrTemplate` {#ssrTemplate}
An HTML template written in [Eta's syntax](https://eta.js.org/docs/syntax#syntax-overview) that will be used to render your application. This can be used to set custom attributes on the `body` tags, additional `meta` tags, customize the `viewport`, etc. Please note that Docusaurus will rely on the template to be correctly structured in order to function properly, once you do customize it, you will have to make sure that your template is compliant with the requirements from upstream.
- Type: `string`
Example:
```js title="docusaurus.config.js"
module.exports = {
ssrTemplate: `<!DOCTYPE html>
<html <%~ it.htmlAttributes %>>
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v<%= it.version %>">
<% it.metaAttributes.forEach((metaAttribute) => { %>
<%~ metaAttribute %>
<% }); %>
<%~ it.headTags %>
<% it.stylesheets.forEach((stylesheet) => { %>
<link rel="stylesheet" href="<%= it.baseUrl %><%= stylesheet %>" />
<% }); %>
<% it.scripts.forEach((script) => { %>
<link rel="preload" href="<%= it.baseUrl %><%= script %>" as="script">
<% }); %>
</head>
<body <%~ it.bodyAttributes %>>
<%~ it.preBodyTags %>
<div id="__docusaurus">
<%~ it.appHtml %>
</div>
<% it.scripts.forEach((script) => { %>
<script src="<%= it.baseUrl %><%= script %>"></script>
<% }); %>
<%~ it.postBodyTags %>
</body>
</html>`,
};
```
### `titleDelimiter` {#titleDelimiter}
- Type: `string`
Will be used as title delimiter in the generated `<title>` tag.
Example:
```js title="docusaurus.config.js"
module.exports = {
titleDelimiter: '🦖', // Defaults to `|`
};
```
### `baseUrlIssueBanner` {#baseUrlIssueBanner}
- Type: `boolean`
When enabled, will show a banner in case your site can't load its CSS or JavaScript files, which is a very common issue, often related to a wrong `baseUrl` in site config.
Example:
```js title="docusaurus.config.js"
module.exports = {
baseUrlIssueBanner: true, // Defaults to `true`
};
```
![A sample base URL issue banner. The style is very raw since the stylesheets failed to load. The text says "Your Docusaurus site did not load properly... Current configured baseUrl = / (default value); We suggest trying baseUrl = /build/](/img/baseUrlIssueBanner.png)
:::caution
This banner needs to inline CSS / JS in case all asset loading fails due to wrong base URL.
If you have a strict [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), you should rather disable it.
:::

View file

@ -0,0 +1,2 @@
label: Miscellaneous
position: 4

View file

@ -0,0 +1,58 @@
---
sidebar_position: 0
slug: /api/misc/create-docusaurus
---
# 📦 create-docusaurus
A scaffolding utility to help you instantly set up a functional Docusaurus app.
## Usage {#usage}
```bash
npx create-docusaurus@latest [name] [template] [rootDir]
```
The `name` argument will be used as the site's path as well as the `name` field in the created app's package.json. It can be an absolute path, or a path relative to `rootDir`.
The `template` argument can be one of the following:
- `classic`: Uses the classic template (recommended)
- `facebook`: Uses the Facebook/Meta template, which contains some Meta-specific setup
- A git repo URL (beginning with `https://` or `git@`), which can be cloned to the destination
- A local file path relative to CWD, which contains the files to be copied to destination
The `rootDir` will be used to resolve the absolute path to the site directory. The default is CWD.
:::caution
This command should be preferably used in an interactive shell so all features are available.
:::
## Options {#options}
### `-t, --typescript` {#typescript}
Used when the template argument is a recognized name. Currently, only `classic` provides a TypeScript variant.
### `-g, --git-strategy` {#git-strategy}
Used when the template argument is a git repo. It needs to be one of:
- `deep`: preserves full git history
- `shallow`: clones with `--depth=1`
- `copy`: does a shallow clone, but does not create a git repo
- `custom`: enter your custom git clone command. We will prompt you for it. You can write something like `git clone --depth 10`, and we will append the repository URL and destination directory.
### `-p, --package-manager` {#package-manager}
Value should be one of `npm`, `yarn`, or `pnpm`. If it's not explicitly provided, Docusaurus will infer one based on:
- The lockfile already present in the CWD (e.g. if you are setting up website in an existing project)
- The command used to invoke `create-docusaurus` (e.g. `npm init`, `npx`, `yarn create`, etc.)
- Interactive prompting, in case all heuristics are not present
### `-s, --skip-install` {#skip-install}
If provided, Docusaurus will not automatically install dependencies after creating the app. The `--package-manager` option is only useful when you are actually installing dependencies.

View file

@ -0,0 +1,72 @@
---
sidebar_position: 1
slug: /api/misc/@docusaurus/eslint-plugin
---
# 📦 eslint-plugin
[ESLint](https://eslint.org/) is a tool that statically analyzes your code and reports problems or suggests best practices through editor hints and command line. Docusaurus provides an ESLint plugin to enforce best Docusaurus practices.
## Installation
```bash npm2yarn
npm install --save-dev @docusaurus/eslint-plugin
```
## Usage
### Recommended config
Add `plugin:@docusaurus/recommended` to the `extends` section of your `.eslintrc` configuration file:
```json title=".eslintrc"
{
"extends": ["plugin:@docusaurus/recommended"]
}
```
This will enable the `@docusaurus` eslint plugin and use the `recommended` config. See [Supported rules](#supported-rules) below for a list of rules that this will enable.
### Manual config
For more fine-grained control, you can also enable the plugin manually and configure the rules you want to use directly:
```json title=".eslintrc"
{
"plugins": ["@docusaurus"],
"rules": {
"@docusaurus/string-literal-i18n-messages": "error",
"@docusaurus/no-untranslated-text": "warn"
}
}
```
## Supported configs
- Recommended: recommended rule set for most Docusaurus sites that should be extended from.
- All: **all** rules enabled. This will change between minor versions, so you should not use this if you want to avoid unexpected breaking changes.
## Supported rules
| Name | Description | |
| --- | --- | --- |
| [`@docusaurus/no-untranslated-text`](./no-untranslated-text.mdx) | Enforce text labels in JSX to be wrapped by translate calls | |
| [`@docusaurus/string-literal-i18n-messages`](./string-literal-i18n-messages.mdx) | Enforce translate APIs to be called on plain text labels | ✅ |
✅ = recommended
## Example configuration
Here's an example configuration:
```js title=".eslintrc.js"
module.exports = {
extends: ['plugin:@docusaurus/recommended'],
rules: {
'@docusaurus/no-untranslated-text': [
'warn',
{ignoredStrings: ['·', '—', '×']},
],
},
};
```

View file

@ -0,0 +1,54 @@
---
slug: /api/misc/@docusaurus/eslint-plugin/no-untranslated-text
---
# no-untranslated-text
import APITable from '@site/src/components/APITable';
Enforce text labels in JSX to be wrapped by translate calls.
When the [i18n feature](../../../i18n/i18n-introduction.mdx) is used, this rule ensures that all labels appearing on the website are translatable, so no string accidentally slips through untranslated.
## Rule Details {#details}
Examples of **incorrect** code for this rule:
```js
// Hello World is not translated
<Component>Hello World</Component>
```
Examples of **correct** code for this rule:
```js
// Hello World is translated
<Component>
<Translate>Hello World</Translate>
</Component>
```
## Rule Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `ignoredStrings` | `string[]` | `[]` | Text labels that only contain strings in this list will not be reported. |
```mdx-code-block
</APITable>
```
## When Not To Use It {#when-not-to-use}
If you're not using the [i18n feature](../../../i18n/i18n-introduction.mdx), you can disable this rule. You can also disable this rule where the text is not supposed to be translated.
## Further Reading {#further-reading}
- https://docusaurus.io/docs/docusaurus-core#translate
- https://docusaurus.io/docs/docusaurus-core#translate-imperative

View file

@ -0,0 +1,50 @@
---
slug: /api/misc/@docusaurus/eslint-plugin/string-literal-i18n-messages
---
# string-literal-i18n-messages
Enforce translate APIs to be called on plain text labels.
Docusaurus offers the [`docusaurus write-translations`](../../../cli.mdx#docusaurus-write-translations-sitedir) API, which statically extracts the text labels marked as translatable. Dynamic values used in `<Translate>` or `translate()` calls will fail to be extracted. This rule will ensure that all translate calls are statically extractable.
## Rule Details {#details}
Examples of **incorrect** code for this rule:
```js
const text = 'Some text to be translated'
// Invalid <Translate> child
<Translate>{text}</Translate>
// Invalid message attribute
translate({message: text})
```
Examples of **correct** code for this rule:
```js
// Valid <Translate> child
<Translate>Some text to be translated</Translate>
// Valid message attribute
translate({message: 'Some text to be translated'})
// Valid <Translate> child using values object as prop
<Translate values={{firstName: 'Sébastien'}}>
{'Welcome, {firstName}! How are you?'}
</Translate>
// Valid message attribute using values object as second argument
translate({message: 'The logo of site {siteName}'}, {siteName: 'Docusaurus'})
```
## When Not To Use It {#when-not-to-use}
If you're not using the [i18n feature](../../../i18n/i18n-introduction.mdx), you can disable this rule.
## Further Reading {#further-reading}
- https://docusaurus.io/docs/docusaurus-core#translate
- https://docusaurus.io/docs/docusaurus-core#translate-imperative

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

View file

@ -0,0 +1,69 @@
---
sidebar_position: 2
slug: /api/misc/@docusaurus/logger
---
# 📦 logger
An encapsulated logger for semantically formatting console messages.
Authors of packages in the Docusaurus ecosystem are encouraged to use this package to provide unified log formats.
## APIs
It exports a single object as default export: `logger`. `logger` has the following properties:
- Some useful colors.
- `red`
- `yellow`
- `green`
- `bold`
- `dim`
- Formatters. These functions all have the signature `(msg: unknown) => string`. Note that their implementations are not guaranteed. You should only care about their semantics.
- `path`: formats a file path.
- `url`: formats a URL.
- `name`: formats an identifier.
- `code`: formats a code snippet.
- `subdue`: subdues the text.
- `num`: formats a number.
- The `interpolate` function. It is a template literal tag. The syntax can be found below.
- Logging functions. All logging functions can both be used as normal functions (similar to the `console.log` family, but only accepts one parameter) or template literal tags.
- `info`: prints information.
- `warn`: prints a warning that should be paid attention to.
- `error`: prints an error (not necessarily halting the program) that signals significant problems.
- `success`: prints a success message.
- The `report` function. It takes a `ReportingSeverity` value (`ignore`, `log`, `warn`, `throw`) and reports a message according to the severity.
:::caution A word on the `error` formatter
Beware that an `error` message, even when it doesn't hang the program, is likely going to cause confusion. When users inspect logs and find an `[ERROR]`, even when the build succeeds, they will assume something is going wrong. Use it sparingly.
Docusaurus only uses `logger.error` when printing messages immediately before throwing an error, or when user has set the reporting severity of `onBrokenLink`, etc. to `"error"`.
In addition, `warn` and `error` will color the **entire** message for better attention. If you are printing large blocks of help text about an error, better use `logger.info`.
:::
### Using the template literal tag
The template literal tag evaluates the template and expressions embedded. `interpolate` returns a new string, while other logging functions prints it. Below is a typical usage:
```js
logger.info`Hello name=${name}! You have number=${money} dollars. Here are the ${
items.length > 1 ? 'items' : 'item'
} on the shelf: ${items}
To buy anything, enter code=${'buy x'} where code=${'x'} is the item's name; to quit, press code=${'Ctrl + C'}.`;
```
An embedded expression is optionally preceded by a flag in the form `[a-z]+=` (a few lowercase letters, followed by an equals sign, directly preceding the embedded expression). If the expression is not preceded by any flag, it's printed out as-is. Otherwise, it's formatted with one of the formatters:
- `path=`: `path`
- `url=`: `url`
- `name=`: `name`
- `code=`: `code`
- `subdue=`: `subdue`
- `number=`: `num`
If the expression is an array, it's formatted by `` `\n- ${array.join('\n- ')}\n` `` (note it automatically gets a leading line end). Each member is formatted by itself and the bullet is not formatted. So you would see the above message printed as:
![Some text output in the terminal, containing array, code, name, and number formatting](./demo.png)

View file

@ -0,0 +1,146 @@
# Plugin Method References
:::caution
This section is a work in progress. Anchor links or even URLs are not guaranteed to be stable.
:::
Plugin APIs are shared by themes and plugins—themes are loaded just like plugins.
## Plugin module {#plugin-module}
Every plugin is imported as a module. The module is expected to have the following members:
- A **default export**: the constructor function for the plugin.
- **Named exports**: the [static methods](./static-methods.mdx) called before plugins are initialized.
## Plugin constructor {#plugin-constructor}
The plugin module's default export is a constructor function with the signature `(context: LoadContext, options: PluginOptions) => Plugin | Promise<Plugin>`.
### `context` {#context}
`context` is plugin-agnostic, and the same object will be passed into all plugins used for a Docusaurus website. The `context` object contains the following fields:
```ts
type LoadContext = {
siteDir: string;
generatedFilesDir: string;
siteConfig: DocusaurusConfig;
outDir: string;
baseUrl: string;
};
```
### `options` {#options}
`options` are the [second optional parameter when the plugins are used](../../using-plugins.mdx#configuring-plugins). `options` are plugin-specific and are specified by users when they use them in `docusaurus.config.js`. If there's a [`validateOptions`](./static-methods.mdx#validateOptions) function exported, the `options` will be validated and normalized beforehand.
Alternatively, if a preset contains the plugin, the preset will then be in charge of passing the correct options into the plugin. It is up to the individual plugin to define what options it takes.
## Example {#example}
Here's a mental model for a presumptuous plugin implementation.
```js
// A JavaScript function that returns an object.
// `context` is provided by Docusaurus. Example: siteConfig can be accessed from context.
// `opts` is the user-defined options.
async function myPlugin(context, opts) {
return {
// A compulsory field used as the namespace for directories to cache
// the intermediate data for each plugin.
// If you're writing your own local plugin, you will want it to
// be unique in order not to potentially conflict with imported plugins.
// A good way will be to add your own project name within.
name: 'docusaurus-my-project-cool-plugin',
async loadContent() {
// The loadContent hook is executed after siteConfig and env has been loaded.
// You can return a JavaScript object that will be passed to contentLoaded hook.
},
async contentLoaded({content, actions}) {
// The contentLoaded hook is done after loadContent hook is done.
// `actions` are set of functional API provided by Docusaurus (e.g. addRoute)
},
async postBuild(props) {
// After docusaurus <build> finish.
},
// TODO
async postStart(props) {
// docusaurus <start> finish
},
// TODO
afterDevServer(app, server) {
// https://webpack.js.org/configuration/dev-server/#devserverbefore
},
// TODO
beforeDevServer(app, server) {
// https://webpack.js.org/configuration/dev-server/#devserverafter
},
configureWebpack(config, isServer, utils, content) {
// Modify internal webpack config. If returned value is an Object, it
// will be merged into the final config using webpack-merge;
// If the returned value is a function, it will receive the config as the 1st argument and an isServer flag as the 2nd argument.
},
getPathsToWatch() {
// Paths to watch.
},
getThemePath() {
// Returns the path to the directory where the theme components can
// be found.
},
getClientModules() {
// Return an array of paths to the modules that are to be imported
// in the client bundle. These modules are imported globally before
// React even renders the initial UI.
},
extendCli(cli) {
// Register an extra command to enhance the CLI of Docusaurus
},
injectHtmlTags({content}) {
// Inject head and/or body HTML tags.
},
async getTranslationFiles({content}) {
// Return translation files
},
translateContent({content, translationFiles}) {
// translate the plugin content here
},
translateThemeConfig({themeConfig, translationFiles}) {
// translate the site themeConfig here
},
async getDefaultCodeTranslationMessages() {
// return default theme translations here
},
};
}
myPlugin.validateOptions = ({options, validate}) => {
const validatedOptions = validate(myValidationSchema, options);
return validatedOptions;
};
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
const validatedThemeConfig = validate(myValidationSchema, options);
return validatedThemeConfig;
};
module.exports = myPlugin;
```

View file

@ -0,0 +1,2 @@
label: Plugin method references
position: 1

View file

@ -0,0 +1,135 @@
---
sidebar_position: 2
---
# Extending infrastructure
Docusaurus has some infrastructure like hot reloading, CLI, and swizzling, that can be extended by external plugins.
## `getPathsToWatch()` {#getPathsToWatch}
Specifies the paths to watch for plugins and themes. The paths are watched by the dev server so that the plugin lifecycles are reloaded when contents in the watched paths change. Note that the plugins and themes modules are initially called with `context` and `options` from Node, which you may use to find the necessary directory information about the site.
Use this for files that are consumed server-side, because theme files are automatically watched by Webpack dev server.
Example:
```js title="docusaurus-plugin/src/index.js"
const path = require('path');
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
// highlight-start
getPathsToWatch() {
const contentPath = path.resolve(context.siteDir, options.path);
return [`${contentPath}/**/*.{ts,tsx}`];
},
// highlight-end
};
};
```
## `extendCli(cli)` {#extendCli}
Register an extra command to enhance the CLI of Docusaurus. `cli` is a [commander](https://www.npmjs.com/package/commander/v/5.1.0) object.
:::caution
The commander version matters! We use commander v5, and make sure you are referring to the right version documentation for available APIs.
:::
Example:
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
// highlight-start
extendCli(cli) {
cli
.command('roll')
.description('Roll a random number between 1 and 1000')
.action(() => {
console.log(Math.floor(Math.random() * 1000 + 1));
});
},
// highlight-end
};
};
```
## `getThemePath()` {#getThemePath}
Returns the path to the directory where the theme components can be found. When your users call `swizzle`, `getThemePath` is called and its returned path is used to find your theme components. Relative paths are resolved against the folder containing the entry point.
For example, your `getThemePath` can be:
```js title="my-theme/src/index.js"
const path = require('path');
module.exports = function (context, options) {
return {
name: 'my-theme',
// highlight-start
getThemePath() {
return './theme';
},
// highlight-end
};
};
```
## `getTypeScriptThemePath()` {#getTypeScriptThemePath}
Similar to `getThemePath()`, it should return the path to the directory where the source code of TypeScript theme components can be found. This path is purely for swizzling TypeScript theme components, and theme components under this path will **not** be resolved by Webpack. Therefore, it is not a replacement for `getThemePath()`. Typically, you can make the path returned by `getTypeScriptThemePath()` be your source directory, and make the path returned by `getThemePath()` be the compiled JavaScript output.
:::tip
For TypeScript theme authors: you are strongly advised to make your compiled output as human-readable as possible. Only strip type annotations and don't transpile any syntaxes, because they will be handled by Webpack's Babel loader based on the targeted browser versions.
You should also format these files with Prettier. Remember—JS files can and will be directly consumed by your users.
:::
Example:
```js title="my-theme/src/index.js"
const path = require('path');
module.exports = function (context, options) {
return {
name: 'my-theme',
// highlight-start
getThemePath() {
// Where compiled JavaScript output lives
return '../lib/theme';
},
getTypeScriptThemePath() {
// Where TypeScript source code lives
return '../src/theme';
},
// highlight-end
};
};
```
## `getSwizzleComponentList()` {#getSwizzleComponentList}
**This is a static method, not attached to any plugin instance.**
Returns a list of stable components that are considered safe for swizzling. These components will be swizzlable without `--danger`. All components are considered unstable by default. If an empty array is returned, all components are considered unstable. If `undefined` is returned, all components are considered stable.
```js title="my-theme/src/index.js"
const swizzleAllowedComponents = [
'CodeBlock',
'DocSidebar',
'Footer',
'NotFound',
'SearchBar',
'hooks/useTheme',
'prism-include-languages',
];
myTheme.getSwizzleComponentList = () => swizzleAllowedComponents;
```

View file

@ -0,0 +1,121 @@
---
sidebar_position: 3
---
# I18n lifecycles
Plugins use these lifecycles to load i18n-related data.
## `getTranslationFiles({content})` {#getTranslationFiles}
Plugins declare the JSON translation files they want to use.
Returns translation files `{path: string, content: ChromeI18nJSON}`:
- `path`: relative to the plugin localized folder `i18n/[locale]/[pluginName]`. Extension `.json` should be omitted to remain generic.
- `content`: using the Chrome i18n JSON format.
These files will be written by the [`write-translations` CLI](../../cli.mdx#docusaurus-write-translations-sitedir) to the plugin i18n subfolder, and will be read in the appropriate locale before calling [`translateContent()`](#translateContent) and [`translateThemeConfig()`](#translateThemeConfig)
Example:
```js
module.exports = function (context, options) {
return {
name: 'my-plugin',
// highlight-start
async getTranslationFiles({content}) {
return [
{
path: 'sidebar-labels',
content: {
someSidebarLabel: {
message: 'Some Sidebar Label',
description: 'A label used in my plugin in the sidebar',
},
someLabelFromContent: content.myLabel,
},
},
];
},
// highlight-end
};
};
```
## `translateContent({content,translationFiles})` {#translateContent}
Translate the plugin content, using the localized translation files.
Returns the localized plugin content.
The `contentLoaded()` lifecycle will be called with the localized plugin content returned by `translateContent()`.
Example:
```js
module.exports = function (context, options) {
return {
name: 'my-plugin',
// highlight-start
translateContent({content, translationFiles}) {
const myTranslationFile = translationFiles.find(
(f) => f.path === 'myTranslationFile',
);
return {
...content,
someContentLabel: myTranslationFile.someContentLabel.message,
};
},
// highlight-end
};
};
```
## `translateThemeConfig({themeConfig,translationFiles})` {#translateThemeConfig}
Translate the site `themeConfig` labels, using the localized translation files.
Returns the localized `themeConfig`.
Example:
```js
module.exports = function (context, options) {
return {
name: 'my-theme',
// highlight-start
translateThemeConfig({themeConfig, translationFiles}) {
const myTranslationFile = translationFiles.find(
(f) => f.path === 'myTranslationFile',
);
return {
...themeConfig,
someThemeConfigLabel: myTranslationFile.someThemeConfigLabel.message,
};
},
// highlight-end
};
};
```
## `async getDefaultCodeTranslationMessages()` {#getDefaultCodeTranslationMessages}
Themes using the `<Translate>` API can provide default code translation messages.
It should return messages in `Record<string, string>`, where keys are translation IDs and values are messages (without the description) localized using the site's current locale.
Example:
```js
module.exports = function (context, options) {
return {
name: 'my-theme',
// highlight-start
async getDefaultCodeTranslationMessages() {
return readJsonFile(`${context.i18n.currentLocale}.json`);
},
// highlight-end
};
};
```

View file

@ -0,0 +1,420 @@
---
sidebar_position: 1
toc_max_heading_level: 4
---
# Lifecycle APIs
During the build, plugins are loaded in parallel to fetch their own contents and render them to routes. Plugins may also configure webpack or post-process the generated files.
## `async loadContent()` {#loadContent}
Plugins should use this lifecycle to fetch from data sources (filesystem, remote API, headless CMS, etc.) or do some server processing. The return value is the content it needs.
For example, this plugin below returns a random integer between 1 to 10 as content.
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
// highlight-start
async loadContent() {
return 1 + Math.floor(Math.random() * 10);
},
// highlight-end
};
};
```
## `async contentLoaded({content, actions})` {#contentLoaded}
The data that was loaded in `loadContent` will be consumed in `contentLoaded`. It can be rendered to routes, registered as global data, etc.
### `content` {#content}
`contentLoaded` will be called _after_ `loadContent` is done. The return value of `loadContent()` will be passed to `contentLoaded` as `content`.
### `actions` {#actions}
`actions` contain three functions:
#### `addRoute(config: RouteConfig): void` {#addRoute}
Create a route to add to the website.
```ts
type RouteConfig = {
path: string;
component: string;
modules?: RouteModules;
routes?: RouteConfig[];
exact?: boolean;
priority?: number;
};
type RouteModules = {
[module: string]: Module | RouteModules | RouteModules[];
};
type Module =
| {
path: string;
__import?: boolean;
query?: ParsedUrlQueryInput;
}
| string;
```
#### `createData(name: string, data: any): Promise<string>` {#createData}
A declarative callback to create static data (generally JSON or string) which can later be provided to your routes as props. Takes the file name and data to be stored, and returns the actual data file's path.
For example, this plugin below creates a `/friends` page which displays `Your friends are: Yangshun, Sebastien`:
```jsx title="website/src/components/Friends.js"
import React from 'react';
export default function FriendsComponent({friends}) {
return <div>Your friends are {friends.join(',')}</div>;
}
```
```js title="docusaurus-friends-plugin/src/index.js"
export default function friendsPlugin(context, options) {
return {
name: 'docusaurus-friends-plugin',
// highlight-start
async contentLoaded({content, actions}) {
const {createData, addRoute} = actions;
// Create friends.json
const friends = ['Yangshun', 'Sebastien'];
const friendsJsonPath = await createData(
'friends.json',
JSON.stringify(friends),
);
// Add the '/friends' routes, and ensure it receives the friends props
addRoute({
path: '/friends',
component: '@site/src/components/Friends.js',
modules: {
// propName -> JSON file path
friends: friendsJsonPath,
},
exact: true,
});
},
// highlight-end
};
}
```
#### `setGlobalData(data: any): void` {#setGlobalData}
This function permits one to create some global plugin data that can be read from any page, including the pages created by other plugins, and your theme layout.
This data becomes accessible to your client-side/theme code through the [`useGlobalData`](../../docusaurus-core.mdx#useGlobalData) and [`usePluginData`](../../docusaurus-core.mdx#usePluginData) hooks.
:::caution
Global data is... global: its size affects the loading time of all pages of your site, so try to keep it small. Prefer `createData` and page-specific data whenever possible.
:::
For example, this plugin below creates a `/friends` page which displays `Your friends are: Yangshun, Sebastien`:
```jsx title="website/src/components/Friends.js"
import React from 'react';
import {usePluginData} from '@docusaurus/useGlobalData';
export default function FriendsComponent() {
const {friends} = usePluginData('docusaurus-friends-plugin');
return <div>Your friends are {friends.join(',')}</div>;
}
```
```js title="docusaurus-friends-plugin/src/index.js"
export default function friendsPlugin(context, options) {
return {
name: 'docusaurus-friends-plugin',
// highlight-start
async contentLoaded({content, actions}) {
const {setGlobalData, addRoute} = actions;
// Create friends global data
setGlobalData({friends: ['Yangshun', 'Sebastien']});
// Add the '/friends' routes
addRoute({
path: '/friends',
component: '@site/src/components/Friends.js',
exact: true,
});
},
// highlight-end
};
}
```
## `configureWebpack(config, isServer, utils, content)` {#configureWebpack}
Modifies the internal webpack config. If the return value is a JavaScript object, it will be merged into the final config using [`webpack-merge`](https://github.com/survivejs/webpack-merge). If it is a function, it will be called and receive `config` as the first argument and an `isServer` flag as the second argument.
:::caution
The API of `configureWebpack` will be modified in the future to accept an object (`configureWebpack({config, isServer, utils, content})`)
:::
### `config` {#config}
`configureWebpack` is called with `config` generated according to client/server build. You may treat this as the base config to be merged with.
### `isServer` {#isServer}
`configureWebpack` will be called both in server build and in client build. The server build receives `true` and the client build receives `false` as `isServer`.
### `utils` {#utils}
`configureWebpack` also receives an util object:
- `getStyleLoaders(isServer: boolean, cssOptions: {[key: string]: any}): Loader[]`
- `getJSLoader(isServer: boolean, cacheOptions?: {}): Loader | null`
You may use them to return your webpack configuration conditionally.
For example, this plugin below modify the webpack config to transpile `.foo` files.
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'custom-docusaurus-plugin',
// highlight-start
configureWebpack(config, isServer, utils) {
const {getJSLoader} = utils;
return {
module: {
rules: [
{
test: /\.foo$/,
use: [getJSLoader(isServer), 'my-custom-webpack-loader'],
},
],
},
};
},
// highlight-end
};
};
```
### `content` {#content-1}
`configureWebpack` will be called both with the content loaded by the plugin.
### Merge strategy {#merge-strategy}
We merge the Webpack configuration parts of plugins into the global Webpack config using [webpack-merge](https://github.com/survivejs/webpack-merge).
It is possible to specify the merge strategy. For example, if you want a webpack rule to be prepended instead of appended:
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) {
return {
// highlight-start
mergeStrategy: {'module.rules': 'prepend'},
module: {rules: [myRuleToPrepend]},
// highlight-end
};
},
};
};
```
Read the [webpack-merge strategy doc](https://github.com/survivejs/webpack-merge#merging-with-strategies) for more details.
### Configuring dev server {#configuring-dev-server}
The dev server can be configured through returning a `devServer` field.
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'custom-docusaurus-plugin',
configureWebpack(config, isServer, utils) {
return {
// highlight-start
devServer: {
open: '/docs', // Opens localhost:3000/docs instead of localhost:3000/
},
// highlight-end
};
},
};
};
```
## `configurePostCss(options)` {#configurePostCss}
Modifies [`postcssOptions` of `postcss-loader`](https://webpack.js.org/loaders/postcss-loader/#postcssoptions) during the generation of the client bundle.
Should return the mutated `postcssOptions`.
By default, `postcssOptions` looks like this:
```js
const postcssOptions = {
ident: 'postcss',
plugins: [require('autoprefixer')],
};
```
Example:
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
// highlight-start
configurePostCss(postcssOptions) {
// Appends new PostCSS plugin.
postcssOptions.plugins.push(require('postcss-import'));
return postcssOptions;
},
// highlight-end
};
};
```
## `postBuild(props)` {#postBuild}
Called when a (production) build finishes.
```ts
interface Props {
siteDir: string;
generatedFilesDir: string;
siteConfig: DocusaurusConfig;
outDir: string;
baseUrl: string;
headTags: string;
preBodyTags: string;
postBodyTags: string;
routesPaths: string[];
plugins: Plugin<any>[];
content: Content;
}
```
Example:
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
// highlight-start
async postBuild({siteConfig = {}, routesPaths = [], outDir}) {
// Print out to console all the rendered routes.
routesPaths.map((route) => {
console.log(route);
});
},
// highlight-end
};
};
```
## `injectHtmlTags({content})` {#injectHtmlTags}
Inject head and/or body HTML tags to Docusaurus generated HTML.
`injectHtmlTags` will be called both with the content loaded by the plugin.
```ts
function injectHtmlTags(): {
headTags?: HtmlTags;
preBodyTags?: HtmlTags;
postBodyTags?: HtmlTags;
};
type HtmlTags = string | HtmlTagObject | (string | HtmlTagObject)[];
type HtmlTagObject = {
/**
* Attributes of the HTML tag
* E.g. `{'disabled': true, 'value': 'demo', 'rel': 'preconnect'}`
*/
attributes?: {
[attributeName: string]: string | boolean;
};
/**
* The tag name e.g. `div`, `script`, `link`, `meta`
*/
tagName: string;
/**
* The inner HTML
*/
innerHTML?: string;
};
```
Example:
```js title="docusaurus-plugin/src/index.js"
module.exports = function (context, options) {
return {
name: 'docusaurus-plugin',
loadContent: async () => {
return {remoteHeadTags: await fetchHeadTagsFromAPI()};
},
// highlight-start
injectHtmlTags({content}) {
return {
headTags: [
{
tagName: 'link',
attributes: {
rel: 'preconnect',
href: 'https://www.github.com',
},
},
...content.remoteHeadTags,
],
preBodyTags: [
{
tagName: 'script',
attributes: {
charset: 'utf-8',
src: '/noflash.js',
},
},
],
postBodyTags: [`<div> This is post body </div>`],
};
},
// highlight-end
};
};
```
## `getClientModules()` {#getClientModules}
Returns an array of paths to the [client modules](../../advanced/client.mdx#client-modules) that are to be imported into the client bundle.
As an example, to make your theme load a `customCss` or `customJs` file path from `options` passed in by the user:
```js title="my-theme/src/index.js"
const path = require('path');
module.exports = function (context, options) {
const {customCss, customJs} = options || {};
return {
name: 'name-of-my-theme',
// highlight-start
getClientModules() {
return [customCss, customJs];
},
// highlight-end
};
};
```

View file

@ -0,0 +1,123 @@
---
sidebar_position: 4
---
# Static methods
Static methods are not part of the plugin instance—they are attached to the constructor function. These methods are used to validate and normalize the plugin options and theme config, which are then used as constructor parameters to initialize the plugin instance.
## `validateOptions({options, validate})` {#validateOptions}
Returns validated and normalized options for the plugin. This method is called before the plugin is initialized. You must return the options since they will be passed to the plugin during initialization.
### `options` {#options}
`validateOptions` is called with `options` passed to plugin for validation and normalization.
### `validate` {#validate}
`validateOptions` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and options as the arguments, returns validated and normalized options. `validate` will automatically handle error and validation config.
:::tip
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of options.
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
:::
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options and return options in case of success.
```js title="my-plugin/src/index.js"
function myPlugin(context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
myPlugin.validateOptions = ({options, validate}) => {
const validatedOptions = validate(myValidationSchema, options);
return validatedOptions;
};
// highlight-end
module.exports = myPlugin;
```
In TypeScript, you can also choose to export this as a separate named export.
```ts title="my-plugin/src/index.ts"
export default function (context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
export function validateOptions({options, validate}) {
const validatedOptions = validate(myValidationSchema, options);
return validatedOptions;
}
// highlight-end
```
## `validateThemeConfig({themeConfig, validate})` {#validateThemeConfig}
Return validated and normalized configuration for the theme.
### `themeConfig` {#themeConfig}
`validateThemeConfig` is called with `themeConfig` provided in `docusaurus.config.js` for validation and normalization.
### `validate` {#validate-1}
`validateThemeConfig` is called with `validate` function which takes a **[Joi](https://www.npmjs.com/package/joi)** schema and `themeConfig` as the arguments, returns validated and normalized options. `validate` will automatically handle error and validation config.
:::tip
[Joi](https://www.npmjs.com/package/joi) is recommended for validation and normalization of theme config.
To avoid mixing Joi versions, use `const {Joi} = require("@docusaurus/utils-validation")`
:::
If you don't use **[Joi](https://www.npmjs.com/package/joi)** for validation you can throw an Error in case of invalid options.
```js title="my-theme/src/index.js"
function myPlugin(context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
myPlugin.validateThemeConfig = ({themeConfig, validate}) => {
const validatedThemeConfig = validate(myValidationSchema, options);
return validatedThemeConfig;
};
// highlight-end
module.exports = validateThemeConfig;
```
In TypeScript, you can also choose to export this as a separate named export.
```ts title="my-theme/src/index.ts"
export default function (context, options) {
return {
name: 'docusaurus-plugin',
// rest of methods
};
}
// highlight-start
export function validateThemeConfig({themeConfig, validate}) {
const validatedThemeConfig = validate(myValidationSchema, options);
return validatedThemeConfig;
}
// highlight-end
```

View file

@ -0,0 +1,5 @@
label: Plugins
position: 2
link:
type: doc
id: api/plugins/plugins-overview # Dogfood using a "qualified id"

View file

@ -0,0 +1,30 @@
---
sidebar_position: 0
id: plugins-overview
sidebar_label: Plugins overview
slug: /api/plugins
---
# Docusaurus plugins
We provide official Docusaurus plugins.
## Content plugins {#content-plugins}
These plugins are responsible for loading your site's content, and creating pages for your theme to render.
- [@docusaurus/plugin-content-docs](./plugin-content-docs.mdx)
- [@docusaurus/plugin-content-blog](./plugin-content-blog.mdx)
- [@docusaurus/plugin-content-pages](./plugin-content-pages.mdx)
## Behavior plugins {#behavior-plugins}
These plugins will add a useful behavior to your Docusaurus site.
- [@docusaurus/plugin-debug](./plugin-debug.mdx)
- [@docusaurus/plugin-sitemap](./plugin-sitemap.mdx)
- [@docusaurus/plugin-pwa](./plugin-pwa.mdx)
- [@docusaurus/plugin-client-redirects](./plugin-client-redirects.mdx)
- [@docusaurus/plugin-ideal-image](./plugin-ideal-image.mdx)
- [@docusaurus/plugin-google-analytics](./plugin-google-analytics.mdx)
- [@docusaurus/plugin-google-gtag](./plugin-google-gtag.mdx)

View file

@ -0,0 +1,127 @@
---
sidebar_position: 4
slug: /api/plugins/@docusaurus/plugin-client-redirects
---
# 📦 plugin-client-redirects
import APITable from '@site/src/components/APITable';
Docusaurus Plugin to generate **client-side redirects**.
This plugin will write additional HTML pages to your static site that redirect the user to your existing Docusaurus pages with JavaScript.
:::caution production only
This plugin is always inactive in development and **only active in production** because it works on the build output.
:::
:::caution
It is better to use server-side redirects whenever possible.
Before using this plugin, you should look if your hosting provider doesn't offer this feature.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-client-redirects
```
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `fromExtensions` | `string[]` | `[]` | The extensions to be removed from the route after redirecting. |
| `toExtensions` | `string[]` | `[]` | The extensions to be appended to the route after redirecting. |
| `redirects` | <code><a href="#RedirectRule">RedirectRule</a>[]</code> | `[]` | The list of redirect rules. |
| `createRedirects` | <code><a href="#CreateRedirectsFn">CreateRedirectsFn</a></code> | `undefined` | A callback to create a redirect rule. Docusaurus query this callback against every path it has created, and use its return value to output more paths. |
```mdx-code-block
</APITable>
```
:::note
This plugin will also read the [`siteConfig.onDuplicateRoutes`](../docusaurus.config.js.mdx#onDuplicateRoutes) config to adjust its logging level when multiple files will be emitted to the same location.
:::
### Types {#types}
#### `RedirectRule` {#RedirectRule}
```ts
type RedirectRule = {
to: string;
from: string | string[];
};
```
:::note
The idea of "from" and "to" is central in this plugin. "From" means a path that you want to _create_, i.e. an extra HTML file that will be written; "to" means a path to want to redirect _to_, usually a route that Docusaurus already knows about.
This is why you can have multiple "from" for the same "to": we will create multiple HTML files that all redirect to the same destination. On the other hand, one "from" can never have more than one "to": the written HTML file needs to have a determinate destination.
:::
#### `CreateRedirectsFn` {#CreateRedirectsFn}
```ts
// The parameter `path` is a route that Docusaurus has already created. It can
// be seen as the "to", and your return value is the "from". Returning a falsy
// value will not create any redirect pages for this particular path.
type CreateRedirectsFn = (path: string) => string[] | string | null | undefined;
```
### Example configuration {#ex-config}
Here's an example configuration:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-client-redirects',
// highlight-start
{
fromExtensions: ['html', 'htm'], // /myPage.html -> /myPage
toExtensions: ['exe', 'zip'], // /myAsset -> /myAsset.zip (if latter exists)
redirects: [
// /docs/oldDoc -> /docs/newDoc
{
to: '/docs/newDoc',
from: '/docs/oldDoc',
},
// Redirect from multiple old paths to the new path
{
to: '/docs/newDoc2',
from: ['/docs/oldDocFrom2019', '/docs/legacyDocFrom2016'],
},
],
createRedirects(existingPath) {
if (existingPath.includes('/community')) {
// Redirect from /docs/team/X to /community/X and /docs/support/X to /community/X
return [
existingPath.replace('/community', '/docs/team'),
existingPath.replace('/community', '/docs/support'),
];
}
return undefined; // Return a falsy value: no redirect created
},
},
// highlight-end
],
],
};
```

View file

@ -0,0 +1,290 @@
---
sidebar_position: 2
slug: /api/plugins/@docusaurus/plugin-content-blog
---
# 📦 plugin-content-blog
import APITable from '@site/src/components/APITable';
Provides the [Blog](blog.mdx) feature and is the default blog plugin for Docusaurus.
:::caution some features production only
The [feed feature](../../blog.mdx#feed) works by extracting the build output, and is **only active in production**.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-content-blog
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the [preset options](#ex-config-preset).
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | `string` | `'blog'` | Path to the blog content directory on the file system, relative to site dir. |
| `editUrl` | <code>string \| <a href="#EditUrlFn">EditUrlFn</a></code> | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativePostPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. |
| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. |
| `blogTitle` | `string` | `'Blog'` | Blog page title for better SEO. |
| `blogDescription` | `string` | `'Blog'` | Blog page meta description for better SEO. |
| `blogSidebarCount` | <code>number \| 'ALL'</code> | `5` | Number of blog post elements to show in the blog sidebar. `'ALL'` to show all blog posts; `0` to disable. |
| `blogSidebarTitle` | `string` | `'Recent posts'` | Title of the blog sidebar. |
| `routeBasePath` | `string` | `'blog'` | URL route for the blog section of your site. **DO NOT** include a trailing slash. Use `/` to put the blog at root path. |
| `tagsBasePath` | `string` | `'tags'` | URL route for the tags section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. |
| `archiveBasePath` | <code>string \| null</code> | `'archive'` | URL route for the archive section of your blog. Will be appended to `routeBasePath`. **DO NOT** include a trailing slash. Use `null` to disable generation of archive. |
| `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. |
| `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. |
| `postsPerPage` | <code>number \| 'ALL'</code> | `10` | Number of posts to show per page in the listing page. Use `'ALL'` to display all posts on one listing page. |
| `blogListComponent` | `string` | `'@theme/BlogListPage'` | Root component of the blog listing page. |
| `blogPostComponent` | `string` | `'@theme/BlogPostPage'` | Root component of each blog post page. |
| `blogTagsListComponent` | `string` | `'@theme/BlogTagsListPage'` | Root component of the tags list page. |
| `blogTagsPostsComponent` | `string` | `'@theme/BlogTagsPostsPage'` | Root component of the "posts containing tag" page. |
| `blogArchiveComponent` | `string` | `'@theme/BlogArchivePage'` | Root component of the blog archive page. |
| `remarkPlugins` | `any[]` | `[]` | Remark plugins passed to MDX. |
| `rehypePlugins` | `any[]` | `[]` | Rehype plugins passed to MDX. |
| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. |
| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. |
| `truncateMarker` | `RegExp` | `/<!--\s*(truncate)\s*-->/` | Truncate marker marking where the summary ends. |
| `showReadingTime` | `boolean` | `true` | Show estimated reading time for the blog post. |
| `readingTime` | `ReadingTimeFn` | The default reading time | A callback to customize the reading time number displayed. |
| `authorsMapPath` | `string` | `'authors.yml'` | Path to the authors map file, relative to the blog content directory. |
| `feedOptions` | _See below_ | `{type: ['rss', 'atom']}` | Blog feed. |
| `feedOptions.type` | <code><a href="#FeedType">FeedType</a> \| <a href="#FeedType">FeedType</a>[] \| 'all' \| null</code> | **Required** | Type of feed to be generated. Use `null` to disable generation. |
| `feedOptions.createFeedItems` | <code><a href="#CreateFeedItemsFn">CreateFeedItemsFn</a> \| undefined</code> | `undefined` | An optional function which can be used to transform and / or filter the items in the feed. |
| `feedOptions.title` | `string` | `siteConfig.title` | Title of the feed. |
| `feedOptions.description` | `string` | <code>\`${siteConfig.title} Blog\`</code> | Description of the feed. |
| `feedOptions.copyright` | `string` | `undefined` | Copyright message. |
| `feedOptions.language` | `string` (See [documentation](http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes) for possible values) | `undefined` | Language metadata of the feed. |
| `sortPosts` | <code>'descending' \| 'ascending' </code> | `'descending'` | Governs the direction of blog post sorting. |
```mdx-code-block
</APITable>
```
### Types {#types}
#### `EditUrlFn` {#EditUrlFn}
```ts
type EditUrlFunction = (params: {
blogDirPath: string;
blogPath: string;
permalink: string;
locale: string;
}) => string | undefined;
```
#### `ReadingTimeFn` {#ReadingTimeFn}
```ts
type ReadingTimeOptions = {
wordsPerMinute: number;
wordBound: (char: string) => boolean;
};
type ReadingTimeCalculator = (params: {
content: string;
frontMatter?: BlogPostFrontMatter & Record<string, unknown>;
options?: ReadingTimeOptions;
}) => number;
type ReadingTimeFn = (params: {
content: string;
frontMatter: BlogPostFrontMatter & Record<string, unknown>;
defaultReadingTime: ReadingTimeCalculator;
}) => number | undefined;
```
#### `FeedType` {#FeedType}
```ts
type FeedType = 'rss' | 'atom' | 'json';
```
#### `CreateFeedItemsFn` {#CreateFeedItemsFn}
```ts
type CreateFeedItemsFn = (params: {
blogPosts: BlogPost[];
siteConfig: DocusaurusConfig;
outDir: string;
defaultCreateFeedItemsFn: CreateFeedItemsFn;
}) => Promise<BlogFeedItem[]>;
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: blog
// Plugin Options: @docusaurus/plugin-content-blog
const config = {
path: 'blog',
// Simple use-case: string editUrl
// editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/',
// Advanced use-case: functional editUrl
editUrl: ({locale, blogDirPath, blogPath, permalink}) =>
`https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`,
editLocalizedFiles: false,
blogTitle: 'Blog title',
blogDescription: 'Blog',
blogSidebarCount: 5,
blogSidebarTitle: 'All our posts',
routeBasePath: 'blog',
include: ['**/*.{md,mdx}'],
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
],
postsPerPage: 10,
blogListComponent: '@theme/BlogListPage',
blogPostComponent: '@theme/BlogPostPage',
blogTagsListComponent: '@theme/BlogTagsListPage',
blogTagsPostsComponent: '@theme/BlogTagsPostsPage',
remarkPlugins: [require('remark-math')],
rehypePlugins: [],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
truncateMarker: /<!--\s*(truncate)\s*-->/,
showReadingTime: true,
feedOptions: {
type: '',
title: '',
description: '',
copyright: '',
language: undefined,
createFeedItems: async (params) => {
const {blogPosts, defaultCreateFeedItems, ...rest} = params;
return defaultCreateFeedItems({
// keep only the 10 most recent blog posts in the feed
blogPosts: blogPosts.filter((item, index) => index < 10),
...rest,
});
},
},
};
```
## Markdown front matter {#markdown-front-matter}
Markdown documents can use the following Markdown front matter metadata fields, enclosed by a line `---` on either side.
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `authors` | `Authors` | `undefined` | List of blog post authors (or unique author). Read the [`authors` guide](../../blog.mdx#blog-post-authors) for more explanations. Prefer `authors` over the `author_*` front matter fields, even for single author blog posts. |
| `author` | `string` | `undefined` | ⚠️ Prefer using `authors`. The blog post author's name. |
| `author_url` | `string` | `undefined` | ⚠️ Prefer using `authors`. The URL that the author's name will be linked to. This could be a GitHub, Twitter, Facebook profile URL, etc. |
| `author_image_url` | `string` | `undefined` | ⚠️ Prefer using `authors`. The URL to the author's thumbnail image. |
| `author_title` | `string` | `undefined` | ⚠️ Prefer using `authors`. A description of the author. |
| `title` | `string` | Markdown title | The blog post title. |
| `date` | `string` | File name or file creation time | The blog post creation date. If not specified, this can be extracted from the file or folder name, e.g, `2021-04-15-blog-post.mdx`, `2021-04-15-blog-post/index.mdx`, `2021/04/15/blog-post.mdx`. Otherwise, it is the Markdown file creation time. |
| `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your post. |
| `draft` | `boolean` | `false` | A boolean flag to indicate that the blog post is work-in-progress. Draft blog posts will only be displayed during development. |
| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. |
| `toc_min_heading_level` | `number` | `2` | The minimum heading level shown in the table of contents. Must be between 2 and 6 and lower or equal to the max value. |
| `toc_max_heading_level` | `number` | `3` | The max heading level shown in the table of contents. Must be between 2 and 6. |
| `keywords` | `string[]` | `undefined` | Keywords meta tag, which will become the `<meta name="keywords" content="keyword1,keyword2,..."/>` in `<head>`, used by search engines. |
| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `<meta name="description" content="..."/>` and `<meta property="og:description" content="..."/>` in `<head>`, used by search engines. |
| `image` | `string` | `undefined` | Cover or thumbnail image that will be used when displaying the link to your post. |
| `slug` | `string` | File path | Allows to customize the blog post URL (`/<routeBasePath>/<slug>`). Support multiple patterns: `slug: my-blog-post`, `slug: /my/path/to/blog/post`, slug: `/`. |
```mdx-code-block
</APITable>
```
```ts
type Tag = string | {label: string; permalink: string};
// An author key references an author from the global plugin authors.yml file
type AuthorKey = string;
type Author = {
key?: AuthorKey;
name: string;
title?: string;
url?: string;
image_url?: string;
};
// The front matter authors field allows various possible shapes
type Authors = AuthorKey | Author | (AuthorKey | Author)[];
```
Example:
```md
---
title: Welcome Docusaurus v2
authors:
- slorber
- yangshun
- name: Joel Marcey
title: Co-creator of Docusaurus 1
url: https://github.com/JoelMarcey
image_url: https://github.com/JoelMarcey.png
tags: [hello, docusaurus-v2]
description: This is my first post on Docusaurus 2.
image: https://i.imgur.com/mErPwqL.png
hide_table_of_contents: false
---
A Markdown blog post
```
## i18n {#i18n}
Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first.
### Translation files location {#translation-files-location}
- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-blog`
- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-blog-[pluginId]`
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir)
- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-blog`
### Example file-system structure {#example-file-system-structure}
```bash
website/i18n/[locale]/docusaurus-plugin-content-blog
│ # translations for website/blog
├── authors.yml
├── first-blog-post.md
├── second-blog-post.md
│ # translations for the plugin options that will be rendered
└── options.json
```

View file

@ -0,0 +1,365 @@
---
sidebar_position: 1
slug: /api/plugins/@docusaurus/plugin-content-docs
---
# 📦 plugin-content-docs
import APITable from '@site/src/components/APITable';
Provides the [Docs](../../guides/docs/docs-introduction.mdx) functionality and is the default docs plugin for Docusaurus.
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-content-docs
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | `string` | `'docs'` | Path to the docs content directory on the file system, relative to site directory. |
| `editUrl` | <code>string \| <a href="#EditUrlFunction">EditUrlFunction</a></code> | `undefined` | Base URL to edit your site. The final URL is computed by `editUrl + relativeDocPath`. Using a function allows more nuanced control for each file. Omitting this variable entirely will disable edit links. |
| `editLocalizedFiles` | `boolean` | `false` | The edit URL will target the localized file, instead of the original unlocalized file. Ignored when `editUrl` is a function. |
| `editCurrentVersion` | `boolean` | `false` | The edit URL will always target the current version doc instead of older versions. Ignored when `editUrl` is a function. |
| `routeBasePath` | `string` | `'docs'` | URL route for the docs section of your site. **DO NOT** include a trailing slash. Use `/` for shipping docs without base path. |
| `tagsBasePath` | `string` | `'tags'` | URL route for the tags list page of your site. It is prepended to the `routeBasePath`. |
| `include` | `string[]` | `['**/*.{md,mdx}']` | Array of glob patterns matching Markdown files to be built, relative to the content path. |
| `exclude` | `string[]` | _See example configuration_ | Array of glob patterns matching Markdown files to be excluded. Serves as refinement based on the `include` option. |
| `sidebarPath` | <code>false \| string</code> | `undefined` | Path to sidebar configuration. Use `false` to disable sidebars, or `undefined` to create a fully autogenerated sidebar. |
| `sidebarCollapsible` | `boolean` | `true` | Whether sidebar categories are collapsible by default. See also [Collapsible categories](/docs/sidebar#collapsible-categories) |
| `sidebarCollapsed` | `boolean` | `true` | Whether sidebar categories are collapsed by default. See also [Expanded categories by default](/docs/sidebar#expanded-categories-by-default) |
| `sidebarItemsGenerator` | <a href="#SidebarGenerator"><code>SidebarGenerator</code></a> | _Omitted_ | Function used to replace the sidebar items of type `'autogenerated'` with real sidebar items (docs, categories, links...). See also [Customize the sidebar items generator](/docs/sidebar#customize-the-sidebar-items-generator) |
| `numberPrefixParser` | <code>boolean \|</code> <a href="#PrefixParser"><code>PrefixParser</code></a> | _Omitted_ | Custom parsing logic to extract number prefixes from file names. Use `false` to disable this behavior and leave the docs untouched, and `true` to use the default parser. See also [Using number prefixes](/docs/sidebar#using-number-prefixes) |
| `docLayoutComponent` | `string` | `'@theme/DocPage'` | Root layout component of each doc page. Provides the version data context, and is not unmounted when switching docs. |
| `docItemComponent` | `string` | `'@theme/DocItem'` | Main doc container, with TOC, pagination, etc. |
| `docTagsListComponent` | `string` | `'@theme/DocTagsListPage'` | Root component of the tags list page |
| `docTagDocListComponent` | `string` | `'@theme/DocTagDocListPage'` | Root component of the "docs containing tag X" page. |
| `docCategoryGeneratedIndexComponent` | `string` | `'@theme/DocCategoryGeneratedIndexPage'` | Root component of the generated category index page. |
| `remarkPlugins` | `any[]` | `[]` | Remark plugins passed to MDX. |
| `rehypePlugins` | `any[]` | `[]` | Rehype plugins passed to MDX. |
| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. |
| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. |
| `showLastUpdateAuthor` | `boolean` | `false` | Whether to display the author who last updated the doc. |
| `showLastUpdateTime` | `boolean` | `false` | Whether to display the last date the doc was updated. |
| `breadcrumbs` | `boolean` | `true` | Enable or disable the breadcrumbs on doc pages. |
| `disableVersioning` | `boolean` | `false` | Explicitly disable versioning even when multiple versions exist. This will make the site only include the current version. Will error if `includeCurrentVersion: false` and `disableVersioning: true`. |
| `includeCurrentVersion` | `boolean` | `true` | Include the current version of your docs. |
| `lastVersion` | `string` | First version in `versions.json` | The version navigated to in priority and displayed by default for docs navbar items. |
| `onlyIncludeVersions` | `string[]` | All versions available | Only include a subset of all available versions. |
| `versions` | <a href="#VersionsConfig"><code>VersionsConfig</code></a> | `{}` | Independent customization of each version's properties. |
```mdx-code-block
</APITable>
```
### Types {#types}
#### `EditUrlFunction` {#EditUrlFunction}
```ts
type EditUrlFunction = (params: {
version: string;
versionDocsDirPath: string;
docPath: string;
permalink: string;
locale: string;
}) => string | undefined;
```
#### `PrefixParser` {#PrefixParser}
```ts
type PrefixParser = (filename: string) => {
filename: string;
numberPrefix?: number;
};
```
#### `SidebarGenerator` {#SidebarGenerator}
```ts
type SidebarGenerator = (generatorArgs: {
/** The sidebar item with type "autogenerated" to be transformed. */
item: {type: 'autogenerated'; dirName: string};
/** Useful metadata for the version this sidebar belongs to. */
version: {contentPath: string; versionName: string};
/** All the docs of that version (unfiltered). */
docs: {
id: string;
title: string;
frontMatter: DocFrontMatter & Record<string, unknown>;
source: string;
sourceDirName: string;
sidebarPosition?: number | undefined;
}[];
/** Number prefix parser configured for this plugin. */
numberPrefixParser: PrefixParser;
/** The default category index matcher which you can override. */
isCategoryIndex: CategoryIndexMatcher;
/**
* key is the path relative to the doc content directory, value is the
* category metadata file's content.
*/
categoriesMetadata: {[filePath: string]: CategoryMetadata};
/**
* Useful to re-use/enhance the default sidebar generation logic from
* Docusaurus.
*/
defaultSidebarItemsGenerator: SidebarGenerator;
// Returns an array of sidebar items — same as what you can declare in
// sidebars.js, except for shorthands. See https://docusaurus.io/docs/sidebar/items
}) => Promise<SidebarItem[]>;
type CategoryIndexMatcher = (param: {
/** The file name, without extension */
fileName: string;
/**
* The list of directories, from lowest level to highest.
* If there's no dir name, directories is ['.']
*/
directories: string[];
/** The extension, with a leading dot */
extension: string;
}) => boolean;
```
#### `VersionsConfig` {#VersionsConfig}
```ts
type VersionConfig = {
/**
* The base path of the version, will be appended to `baseUrl` +
* `routeBasePath`.
*/
path?: string;
/** The label of the version to be used in badges, dropdowns, etc. */
label?: string;
/** The banner to show at the top of a doc of that version. */
banner?: 'none' | 'unreleased' | 'unmaintained';
/** Show a badge with the version label at the top of each doc. */
badge?: boolean;
/** Prevents search engines from indexing this version */
noIndex?: boolean;
/** Add a custom class name to the <html> element of each doc */
className?: string;
};
type VersionsConfig = {[versionName: string]: VersionConfig};
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: docs
// Plugin Options: @docusaurus/plugin-content-docs
const config = {
path: 'docs',
breadcrumbs: true,
// Simple use-case: string editUrl
// editUrl: 'https://github.com/facebook/docusaurus/edit/main/website/',
// Advanced use-case: functional editUrl
editUrl: ({versionDocsDirPath, docPath}) =>
`https://github.com/facebook/docusaurus/edit/main/website/${versionDocsDirPath}/${docPath}`,
editLocalizedFiles: false,
editCurrentVersion: false,
routeBasePath: 'docs',
include: ['**/*.md', '**/*.mdx'],
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
],
sidebarPath: 'sidebars.js',
async sidebarItemsGenerator({
defaultSidebarItemsGenerator,
numberPrefixParser,
item,
version,
docs,
isCategoryIndex,
}) {
// Use the provided data to generate a custom sidebar slice
return [
{type: 'doc', id: 'intro'},
{
type: 'category',
label: 'Tutorials',
items: [
{type: 'doc', id: 'tutorial1'},
{type: 'doc', id: 'tutorial2'},
],
},
];
},
numberPrefixParser(filename) {
// Implement your own logic to extract a potential number prefix
const numberPrefix = findNumberPrefix(filename);
// Prefix found: return it with the cleaned filename
if (numberPrefix) {
return {
numberPrefix,
filename: filename.replace(prefix, ''),
};
}
// No number prefix found
return {numberPrefix: undefined, filename};
},
docLayoutComponent: '@theme/DocPage',
docItemComponent: '@theme/DocItem',
remarkPlugins: [require('remark-math')],
rehypePlugins: [],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
showLastUpdateAuthor: false,
showLastUpdateTime: false,
disableVersioning: false,
includeCurrentVersion: true,
lastVersion: undefined,
versions: {
current: {
label: 'Android SDK v2.0.0 (WIP)',
path: 'android-2.0.0',
banner: 'none',
},
'1.0.0': {
label: 'Android SDK v1.0.0',
path: 'android-1.0.0',
banner: 'unmaintained',
},
},
onlyIncludeVersions: ['current', '1.0.0', '2.0.0'],
};
```
## Markdown front matter {#markdown-front-matter}
Markdown documents can use the following Markdown front matter metadata fields, enclosed by a line `---` on either side.
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | file path (including folders, without the extension) | A unique document ID. |
| `title` | `string` | Markdown title or `id` | The text title of your document. Used for the page metadata and as a fallback value in multiple places (sidebar, next/previous buttons...). Automatically added at the top of your doc if it does not contain any Markdown title. |
| `pagination_label` | `string` | `sidebar_label` or `title` | The text used in the document next/previous buttons for this document. |
| `sidebar_label` | `string` | `title` | The text shown in the document sidebar for this document. |
| `sidebar_position` | `number` | Default ordering | Controls the position of a doc inside the generated sidebar slice when using `autogenerated` sidebar items. See also [Autogenerated sidebar metadata](/docs/sidebar#autogenerated-sidebar-metadata). |
| `sidebar_class_name` | `string` | `undefined` | Gives the corresponding sidebar label a special class name when using autogenerated sidebars. |
| `sidebar_custom_props` | `string` | `undefined` | Assign custom metadata to the sidebar item referencing this doc. |
| `displayed_sidebar` | `string` | `undefined` | Force the display of a given sidebar when browsing the current document. Read the [multiple sidebars guide](../../guides/docs/sidebar/multiple-sidebars.mdx) for details. |
| `hide_title` | `boolean` | `false` | Whether to hide the title at the top of the doc. It only hides a title declared through the front matter, and have no effect on a Markdown title at the top of your document. |
| `hide_table_of_contents` | `boolean` | `false` | Whether to hide the table of contents to the right. |
| `toc_min_heading_level` | `number` | `2` | The minimum heading level shown in the table of contents. Must be between 2 and 6 and lower or equal to the max value. |
| `toc_max_heading_level` | `number` | `3` | The max heading level shown in the table of contents. Must be between 2 and 6. |
| `pagination_next` | <code>string \| null</code> | Next doc in the sidebar | The ID of the documentation you want the "Next" pagination to link to. Use `null` to disable showing "Next" for this page. |
| `pagination_prev` | <code>string \| null</code> | Previous doc in the sidebar | The ID of the documentation you want the "Previous" pagination to link to. Use `null` to disable showing "Previous" for this page. |
| `parse_number_prefixes` | `boolean` | `numberPrefixParser` plugin option | Whether number prefix parsing is disabled on this doc. See also [Using number prefixes](/docs/sidebar#using-number-prefixes). |
| `custom_edit_url` | `string` | Computed using the `editUrl` plugin option | The URL for editing this document. |
| `keywords` | `string[]` | `undefined` | Keywords meta tag for the document page, for search engines. |
| `description` | `string` | The first line of Markdown content | The description of your document, which will become the `<meta name="description" content="..."/>` and `<meta property="og:description" content="..."/>` in `<head>`, used by search engines. |
| `image` | `string` | `undefined` | Cover or thumbnail image that will be used when displaying the link to your post. |
| `slug` | `string` | File path | Allows to customize the document URL (`/<routeBasePath>/<slug>`). Support multiple patterns: `slug: my-doc`, `slug: /my/path/myDoc`, `slug: /`. |
| `tags` | `Tag[]` | `undefined` | A list of strings or objects of two string fields `label` and `permalink` to tag to your docs. |
| `draft` | `boolean` | `false` | A boolean flag to indicate that a document is a work-in-progress. Draft documents will only be displayed during development. |
| `last_update` | `FileChange` | `undefined` | Allows overriding the last updated author and/or date. Date can be any [parsable date string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse). |
```mdx-code-block
</APITable>
```
```ts
type Tag = string | {label: string; permalink: string};
```
```ts
type FileChange = {date: string; author: string};
```
Example:
```md
---
id: doc-markdown
title: Docs Markdown Features
hide_title: false
hide_table_of_contents: false
sidebar_label: Markdown
sidebar_position: 3
pagination_label: Markdown features
custom_edit_url: https://github.com/facebook/docusaurus/edit/main/docs/api-doc-markdown.md
description: How do I find you when I cannot solve this problem
keywords:
- docs
- docusaurus
image: https://i.imgur.com/mErPwqL.png
slug: /myDoc
last_update:
date: 1/1/2000
author: custom author name
---
# Markdown Features
My Document Markdown content
```
## i18n {#i18n}
Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first.
### Translation files location {#translation-files-location}
- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-docs`
- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-docs-[pluginId]`
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir)
- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-docs/[versionName]`
### Example file-system structure {#example-file-system-structure}
```bash
website/i18n/[locale]/docusaurus-plugin-content-docs
│ # translations for website/docs
├── current
│ ├── api
│ │ └── config.md
│ └── getting-started.md
├── current.json
│ # translations for website/versioned_docs/version-1.0.0
├── version-1.0.0
│ ├── api
│ │ └── config.md
│ └── getting-started.md
└── version-1.0.0.json
```

View file

@ -0,0 +1,101 @@
---
sidebar_position: 3
slug: /api/plugins/@docusaurus/plugin-content-pages
---
# 📦 plugin-content-pages
import APITable from '@site/src/components/APITable';
The default pages plugin for Docusaurus. The classic template ships with this plugin with default configurations. This plugin provides [creating pages](guides/creating-pages.mdx) functionality.
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-content-pages
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `path` | `string` | `'src/pages'` | Path to data on filesystem relative to site dir. Components in this directory will be automatically converted to pages. |
| `routeBasePath` | `string` | `'/'` | URL route for the pages section of your site. **DO NOT** include a trailing slash. |
| `include` | `string[]` | `['**/*.{js,jsx,ts,tsx,md,mdx}']` | Matching files will be included and processed. |
| `exclude` | `string[]` | _See example configuration_ | No route will be created for matching files. |
| `mdxPageComponent` | `string` | `'@theme/MDXPage'` | Component used by each MDX page. |
| `remarkPlugins` | `[]` | `any[]` | Remark plugins passed to MDX. |
| `rehypePlugins` | `[]` | `any[]` | Rehype plugins passed to MDX. |
| `beforeDefaultRemarkPlugins` | `any[]` | `[]` | Custom Remark plugins passed to MDX before the default Docusaurus Remark plugins. |
| `beforeDefaultRehypePlugins` | `any[]` | `[]` | Custom Rehype plugins passed to MDX before the default Docusaurus Rehype plugins. |
```mdx-code-block
</APITable>
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: pages
// Plugin Options: @docusaurus/plugin-content-pages
const config = {
path: 'src/pages',
routeBasePath: '',
include: ['**/*.{js,jsx,ts,tsx,md,mdx}'],
exclude: [
'**/_*.{js,jsx,ts,tsx,md,mdx}',
'**/_*/**',
'**/*.test.{js,jsx,ts,tsx}',
'**/__tests__/**',
],
mdxPageComponent: '@theme/MDXPage',
remarkPlugins: [require('remark-math')],
rehypePlugins: [],
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [],
};
```
## i18n {#i18n}
Read the [i18n introduction](../../i18n/i18n-introduction.mdx) first.
### Translation files location {#translation-files-location}
- **Base path**: `website/i18n/[locale]/docusaurus-plugin-content-pages`
- **Multi-instance path**: `website/i18n/[locale]/docusaurus-plugin-content-pages-[pluginId]`
- **JSON files**: extracted with [`docusaurus write-translations`](../../cli.mdx#docusaurus-write-translations-sitedir)
- **Markdown files**: `website/i18n/[locale]/docusaurus-plugin-content-pages`
### Example file-system structure {#example-file-system-structure}
```bash
website/i18n/[locale]/docusaurus-plugin-content-pages
│ # translations for website/src/pages
├── first-markdown-page.md
└── second-markdown-page.md
```

View file

@ -0,0 +1,108 @@
---
sidebar_position: 5
slug: /api/plugins/@docusaurus/plugin-debug
---
# 📦 plugin-debug
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
The debug plugin will display useful debug information at [http://localhost:3000/\_\_docusaurus/debug](http://localhost:3000/__docusaurus/debug).
It is mostly useful for plugin authors, that will be able to inspect more easily the content of the `.docusaurus` folder (like the creates routes), but also be able to inspect data structures that are never written to disk, like the plugin data loaded through the `contentLoaded` lifecycle.
:::info
If you use the plugin via the classic preset, the preset will **enable the plugin in development and disable it in production** by default (`debug: undefined`) to avoid exposing potentially sensitive information. You can use `debug: true` to always enable it or `debug: false` to always disable it.
If you use a standalone plugin, you may need to achieve the same effect by checking the environment:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
// highlight-next-line
process.env.NODE_ENV === 'production' && '@docusaurus/plugin-debug',
].filter(Boolean),
};
```
:::
:::note
If you report a bug, we will probably ask you to have this plugin turned on in the production, so that we can inspect your deployment config more easily.
If you don't have any sensitive information, you can keep it on in production [like we do](/__docusaurus/debug).
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-debug
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
This plugin currently has no options.
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```mdx-code-block
<Tabs groupId="api-config-ex">
<TabItem value="preset" label="Preset options">
```
If you use a preset, configure this plugin through the [preset options](../../using-plugins.mdx#docusauruspreset-classic):
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
// highlight-next-line
debug: true, // This will enable the plugin in production
},
],
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="plugin" label="Plugin Options">
```
If you are using a standalone plugin, provide options directly to the plugin:
```js title="docusaurus.config.js"
module.exports = {
// highlight-next-line
plugins: ['@docusaurus/plugin-debug'],
};
```
```mdx-code-block
</TabItem>
</Tabs>
```

View file

@ -0,0 +1,77 @@
---
sidebar_position: 6
slug: /api/plugins/@docusaurus/plugin-google-analytics
---
# 📦 plugin-google-analytics
import APITable from '@site/src/components/APITable';
The default [Google Analytics](https://developers.google.com/analytics/devguides/collection/analyticsjs/) plugin. It is a JavaScript library for measuring how users interact with your website **in the production build**. If you are using Google Analytics 4 you might need to consider using [plugin-google-gtag](./plugin-google-gtag.mdx) instead.
:::danger Deprecated
This plugin is **deprecated**, and will become useless on July 1, 2023.
Google is [moving away from Universal Analytics](https://blog.google/products/marketingplatform/analytics/prepare-for-future-with-google-analytics-4/).
If you are still using this plugin with a `UA-*` tracking id, you should create a Google Analytics 4 account as soon as possible, and use [`@docusaurus/plugin-google-gtag`](./plugin-google-gtag.mdx) instead of this plugin. More details [here](https://github.com/facebook/docusaurus/issues/7221).
:::
:::caution production only
This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-google-analytics
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `trackingID` | `string` | **Required** | The tracking ID of your analytics service. |
| `anonymizeIP` | `boolean` | `false` | Whether the IP should be anonymized when sending requests. |
```mdx-code-block
</APITable>
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: googleAnalytics
// Plugin Options: @docusaurus/plugin-google-analytics
const config = {
trackingID: 'UA-141789564-1',
anonymizeIP: true,
};
```

View file

@ -0,0 +1,73 @@
---
sidebar_position: 7
slug: /api/plugins/@docusaurus/plugin-google-gtag
---
# 📦 plugin-google-gtag
import APITable from '@site/src/components/APITable';
The default [Global Site Tag (gtag.js)](https://developers.google.com/analytics/devguides/collection/gtagjs/) plugin. It is a JavaScript tagging framework and API that allows you to send event data to Google Analytics, Google Ads, and Google Marketing Platform. This section describes how to configure a Docusaurus site to enable global site tag for Google Analytics.
:::tip
You can use [Google's Tag Assistant](https://tagassistant.google.com/) tool to check if your gtag is set up correctly!
:::
:::caution production only
This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-google-gtag
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `trackingID` | <code>string \| string[]</code> | **Required** | The tracking ID of your gtag service. It is possible to provide multiple ids. |
| `anonymizeIP` | `boolean` | `false` | Whether the IP should be anonymized when sending requests. |
```mdx-code-block
</APITable>
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: gtag
// Plugin Options: @docusaurus/plugin-google-gtag
const config = {
trackingID: 'G-999X9XX9XX',
anonymizeIP: true,
};
```

View file

@ -0,0 +1,70 @@
---
sidebar_position: 8
slug: /api/plugins/@docusaurus/plugin-google-tag-manager
---
# 📦 plugin-google-tag-manager
import APITable from '@site/src/components/APITable';
A plugin for adding [Google Tag Manager (gtm.js)](https://developers.google.com/tag-platform/tag-manager) to a Docusaurus site. Use this plugin in conjunction with the standard [gtag plugin](./plugin-google-gtag.mdx) for in-depth analysis of how users are using your site.
:::tip
You can use [Google's Tag Assistant](https://tagassistant.google.com/) tool to check if tag manager is set up correctly!
:::
:::caution production only
This plugin is always inactive in development and **only active in production** to avoid polluting the analytics statistics.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-google-tag-manager
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the preset options.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `containerId` | `string` | **Required** | Your Tag Manager container Id (usually starts with `GTM-`). |
```mdx-code-block
</APITable>
```
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: googleTagManager
// Plugin Options: @docusaurus/plugin-google-tag-manager
const config = {
containerId: 'GTM-12345',
};
```

View file

@ -0,0 +1,83 @@
---
sidebar_position: 8
slug: /api/plugins/@docusaurus/plugin-ideal-image
---
# 📦 plugin-ideal-image
import APITable from '@site/src/components/APITable';
Docusaurus Plugin to generate an almost ideal image (responsive, lazy-loading, and low quality placeholder).
:::info
By default, the plugin is **inactive in development** so you could always view full-scale images. If you want to debug the ideal image behavior, you could set the [`disableInDev`](#disableInDev) option to `false`.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-ideal-image
```
## Usage {#usage}
This plugin supports the PNG and JPG formats only.
```jsx
import Image from '@theme/IdealImage';
import thumbnail from './path/to/img.png';
// your React code
<Image img={thumbnail} />
// or
<Image img={require('./path/to/img.png')} />
```
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | `ideal-img/[name].[hash:hex:7].[width].[ext]` | Filename template for output files. |
| `sizes` | `number[]` | _original size_ | Specify all widths you want to use. If a specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up). |
| `size` | `number` | _original size_ | Specify one width you want to use; if the specified size exceeds the original image's width, the latter will be used (i.e. images won't be scaled up) |
| `min` | `number` | | As an alternative to manually specifying `sizes`, you can specify `min`, `max` and `steps`, and the sizes will be generated for you. |
| `max` | `number` | | See `min` above |
| `steps` | `number` | `4` | Configure the number of images generated between `min` and `max` (inclusive) |
| `quality` | `number` | `85` | JPEG compression quality |
| `disableInDev` | `boolean` | `true` | You can test ideal image behavior in dev mode by setting this to `false`. **Tip**: use [network throttling](https://www.browserstack.com/guide/how-to-perform-network-throttling-in-chrome) in your browser to simulate slow networks. |
```mdx-code-block
</APITable>
```
### Example configuration {#ex-config}
Here's an example configuration:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-ideal-image',
// highlight-start
{
quality: 70,
max: 1030, // max resized image's size.
min: 640, // min resized image's size. if original is lower, use that size.
steps: 2, // the max number of images generated between min and max (inclusive)
disableInDev: false,
},
// highlight-end
],
],
};
```

View file

@ -0,0 +1,303 @@
---
sidebar_position: 9
slug: /api/plugins/@docusaurus/plugin-pwa
---
# 📦 plugin-pwa
Docusaurus Plugin to add PWA support using [Workbox](https://developers.google.com/web/tools/workbox). This plugin generates a [Service Worker](https://developers.google.com/web/fundamentals/primers/service-workers) in production build only, and allows you to create fully PWA-compliant documentation site with offline and installation support.
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-pwa
```
## Configuration {#configuration}
Create a [PWA manifest](https://web.dev/add-manifest/) at `./static/manifest.json`.
Modify `docusaurus.config.js` with a minimal PWA config, like:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
debug: true,
offlineModeActivationStrategies: [
'appInstalled',
'standalone',
'queryString',
],
pwaHead: [
{
tagName: 'link',
rel: 'icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'manifest',
href: '/manifest.json', // your PWA manifest
},
{
tagName: 'meta',
name: 'theme-color',
content: 'rgb(37, 194, 160)',
},
],
},
],
],
};
```
## Progressive Web App {#progressive-web-app}
Having a service worker installed is not enough to make your application a PWA. You'll need to at least include a [Web App Manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) and have the correct tags in `<head>` ([Options > pwaHead](#pwahead)).
After deployment, you can use [Lighthouse](https://developers.google.com/web/tools/lighthouse) to run an audit on your site.
For a more exhaustive list of what it takes for your site to be a PWA, refer to the [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist)
## App installation support {#app-installation-support}
If your browser supports it, you should be able to install a Docusaurus site as an app.
![A screen recording of the installation process. A button appears in the address bar of the browser, which displays a dialog asking "install this application?" when clicked. After clicking the "Install" button, a new application is opened in the operating system, opening to the Docusaurus homepage.](/img/pwa_install.gif)
:::note
App installation requires the HTTPS protocol and a valid manifest.
:::
## Offline mode (precaching) {#offline-mode-precaching}
We enable users to browse a Docusaurus site offline, by using service-worker precaching.
The [workbox-precaching](https://developers.google.com/web/tools/workbox/modules/workbox-precaching) page explains the idea:
> One feature of service workers is the ability to save a set of files to the cache when the service worker is installing. This is often referred to as "precaching", since you are caching content ahead of the service worker being used.
>
> The main reason for doing this is that it gives developers control over the cache, meaning they can determine when and how long a file is cached as well as serve it to the browser without going to the network, meaning it can be used to create web apps that work offline.
>
> Workbox takes a lot of the heavy lifting out of precaching by simplifying the API and ensuring assets are downloaded efficiently.
By default, offline mode is enabled when the site is installed as an app. See the `offlineModeActivationStrategies` option for details.
After the site has been precached, the service worker will serve cached responses for later visits. When a new build is deployed along with a new service worker, the new one will begin installing and eventually move to a waiting state. During this waiting state, a reload popup will show and ask the user to reload the page for new content. Until the user either clears the application cache or clicks the `reload` button on the popup, the service worker will continue serving the old content.
:::caution
Offline mode / precaching requires downloading all the static assets of the site ahead of time, and can consume unnecessary bandwidth. It may not be a good idea to activate it for all kind of sites.
:::
## Options {#options}
### `debug` {#debug}
- Type: `boolean`
- Default: `false`
Turn debug mode on:
- Workbox logs
- Additional Docusaurus logs
- Unoptimized SW file output
- Source maps
### `offlineModeActivationStrategies` {#offlinemodeactivationstrategies}
- Type: `('appInstalled' | 'mobile' | 'saveData'| 'queryString' | 'always')[]`
- Default: `['appInstalled', 'queryString', 'standalone']`
Strategies used to turn the offline mode on:
- `appInstalled`: activates for users having installed the site as an app (not 100% reliable)
- `standalone`: activates for users running the app as standalone (often the case once a PWA is installed)
- `queryString`: activates if queryString contains `offlineMode=true` (convenient for PWA debugging)
- `mobile`: activates for mobile users (`width <= 996px`)
- `saveData`: activates for users with `navigator.connection.saveData === true`
- `always`: activates for all users
:::caution
Use this carefully: some users may not like to be forced to use the offline mode.
:::
:::danger
It is not possible to detect if an as a PWA in a very reliable way.
The `appinstalled` event has been [removed from the specification](https://github.com/w3c/manifest/pull/836), and the [`navigator.getInstalledRelatedApps()`](https://web.dev/get-installed-related-apps/) API is only supported in recent Chrome versions and require `related_applications` declared in the manifest.
The [`standalone` strategy](https://petelepage.com/blog/2019/07/is-my-pwa-installed/) is a nice fallback to activate the offline mode (at least when running the installed app).
:::
### `injectManifestConfig` {#injectmanifestconfig}
[Workbox options](https://developer.chrome.com/docs/workbox/reference/workbox-build/#type-InjectManifestOptions) to pass to `workbox.injectManifest()`. This gives you control over which assets will be precached, and be available offline.
- Type: `InjectManifestOptions`
- Default: `{}`
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
injectManifestConfig: {
manifestTransforms: [
//...
],
modifyURLPrefix: {
//...
},
// We already add regular static assets (HTML, images...) to be available offline
// You can add more files according to your needs
globPatterns: ['**/*.{pdf,docx,xlsx}'],
// ...
},
},
],
],
};
```
### `pwaHead` {#pwahead}
- Type: `({ tagName: string; [attributeName: string]: string })[]`
- Default: `[]`
Array of objects containing `tagName` and key-value pairs for attributes to inject into the `<head>` tag. Technically you can inject any head tag through this, but it's ideally used for tags to make your site PWA compliant. Here's a list of tag to make your app fully compliant:
```js
module.exports = {
plugins: [
[
'@docusaurus/plugin-pwa',
{
pwaHead: [
{
tagName: 'link',
rel: 'icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'manifest',
href: '/manifest.json',
},
{
tagName: 'meta',
name: 'theme-color',
content: 'rgb(37, 194, 160)',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-capable',
content: 'yes',
},
{
tagName: 'meta',
name: 'apple-mobile-web-app-status-bar-style',
content: '#000',
},
{
tagName: 'link',
rel: 'apple-touch-icon',
href: '/img/docusaurus.png',
},
{
tagName: 'link',
rel: 'mask-icon',
href: '/img/docusaurus.svg',
color: 'rgb(37, 194, 160)',
},
{
tagName: 'meta',
name: 'msapplication-TileImage',
content: '/img/docusaurus.png',
},
{
tagName: 'meta',
name: 'msapplication-TileColor',
content: '#000',
},
],
},
],
],
};
```
### `swCustom` {#swcustom}
- Type: `string | undefined`
- Default: `undefined`
Useful for additional Workbox rules. You can do whatever a service worker can do here, and use the full power of workbox libraries. The code is transpiled, so you can use modern ES6+ syntax here.
For example, to cache files from external routes:
```js
import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';
// default fn export receiving some useful params
export default function swCustom(params) {
const {
debug, // :boolean
offlineMode, // :boolean
} = params;
// Cache responses from external resources
registerRoute((context) => {
return [
/graph\.facebook\.com\/.*\/picture/,
/netlify\.com\/img/,
/avatars1\.githubusercontent/,
].some((regex) => context.url.href.match(regex));
}, new StaleWhileRevalidate());
}
```
The module should have a `default` function export, and receives some params.
### `swRegister` {#swregister}
- Type: `string | false`
- Default: `'docusaurus-plugin-pwa/src/registerSW.js'`
Adds an entry before the Docusaurus app so that registration can happen before the app runs. The default `registerSW.js` file is enough for simple registration.
Passing `false` will disable registration entirely.
## Manifest example {#manifest-example}
The Docusaurus site manifest can serve as an inspiration:
```mdx-code-block
import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-json">
{JSON.stringify(require("@site/static/manifest.json"),null,2)}
</CodeBlock>
```
## Customizing reload popup {#customizing-reload-popup}
The `@theme/PwaReloadPopup` component is rendered when a new service worker is waiting to be installed, and we suggest a reload to the user. You can [swizzle](../../swizzling.mdx) this component and implement your own UI. It will receive an `onReload` callback as props, which should be called when the `reload` button is clicked. This will tell the service worker to install the waiting service worker and reload the page.
The default theme includes an implementation for the reload popup and uses [Infima Alerts](https://infima.dev/docs/components/alert).
![A screen recording of the reload process. An alert box shows in the bottom right of the window, saying "New content available". After clicking the "Refresh" button, the page's main heading changes from "Introduction" to "PWA :))".](/img/pwa_reload.gif)
Your component can render `null`, but this is not recommended: users won't have a way to get up-to-date content.

View file

@ -0,0 +1,82 @@
---
sidebar_position: 10
slug: /api/plugins/@docusaurus/plugin-sitemap
---
# 📦 plugin-sitemap
import APITable from '@site/src/components/APITable';
This plugin creates sitemaps for your site so that search engine crawlers can crawl your site more accurately.
:::caution production only
This plugin is always inactive in development and **only active in production** because it works on the build output.
:::
## Installation {#installation}
```bash npm2yarn
npm install --save @docusaurus/plugin-sitemap
```
:::tip
If you use the preset `@docusaurus/preset-classic`, you don't need to install this plugin as a dependency.
You can configure this plugin through the [preset options](#ex-config-preset).
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `changefreq` | `string` | `'weekly'` | See [sitemap docs](https://www.sitemaps.org/protocol.html#xmlTagDefinitions) |
| `priority` | `number` | `0.5` | See [sitemap docs](https://www.sitemaps.org/protocol.html#xmlTagDefinitions) |
| `ignorePatterns` | `string[]` | `[]` | A list of glob patterns; matching route paths will be filtered from the sitemap. Note that you may need to include the base URL in here. |
| `filename` | `string` | `sitemap.xml` | The path to the created sitemap file, relative to the output directory. Useful if you have two plugin instances outputting two files. |
```mdx-code-block
</APITable>
```
:::info
This plugin also respects some site config:
- [`noIndex`](../docusaurus.config.js.mdx#noIndex): results in no sitemap generated
- [`trailingSlash`](../docusaurus.config.js.mdx#trailingSlash): determines if the URLs in the sitemap have trailing slashes
:::
### Example configuration {#ex-config}
You can configure this plugin through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: sitemap
// Plugin Options: @docusaurus/plugin-sitemap
const config = {
changefreq: 'weekly',
priority: 0.5,
ignorePatterns: ['/tags/**'],
filename: 'sitemap.xml',
};
```
You can find your sitemap at `/sitemap.xml`.

View file

@ -0,0 +1,5 @@
label: Themes
position: 3
link:
type: doc
id: themes-overview # Dogfood using a "local id"

View file

@ -0,0 +1,34 @@
---
sidebar_position: 0
id: themes-overview
sidebar_label: Themes overview
slug: /api/themes
---
# Docusaurus themes
We provide official Docusaurus themes.
## Main themes {#main-themes}
The main themes implement the user interface for the [docs](../plugins/plugin-content-docs.mdx), [blog](../plugins/plugin-content-blog.mdx) and [pages](../plugins/plugin-content-pages.mdx) plugins.
- [@docusaurus/theme-classic](./theme-classic.mdx)
- 🚧 other themes are planned
:::caution
The goal is to have all themes share the exact same features, user-experience and configuration.
Only the UI design and underlying styling framework should change, and you should be able to change theme easily.
We are not there yet: only the classic theme is production ready.
:::
## Enhancement themes {#enhancement-themes}
These themes will enhance the existing main themes with additional user-interface related features.
- [@docusaurus/theme-live-codeblock](./theme-live-codeblock.mdx)
- [@docusaurus/theme-search-algolia](./theme-search-algolia.mdx)

View file

@ -0,0 +1,63 @@
---
sidebar_position: 2
slug: /api/themes/@docusaurus/theme-classic
---
import APITable from '@site/src/components/APITable';
# 📦 theme-classic
The classic theme for Docusaurus.
You can refer to the [theme configuration page](theme-configuration.mdx) for more details on the configuration.
```bash npm2yarn
npm install --save @docusaurus/theme-classic
```
:::tip
If you have installed `@docusaurus/preset-classic`, you don't need to install it as a dependency.
:::
## Configuration {#configuration}
Accepted fields:
```mdx-code-block
<APITable>
```
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `customCss` | <code>string[] \| string</code> | `[]` | Stylesheets to be imported globally as [client modules](../../advanced/client.mdx#client-modules). Relative paths are resolved against the site directory. |
```mdx-code-block
</APITable>
```
:::note
Most configuration for the theme is done in `themeConfig`, which can be found in [theme configuration](./theme-configuration.mdx).
:::
### Example configuration {#ex-config}
You can configure this theme through preset options or plugin options.
:::tip
Most Docusaurus users configure this plugin through the preset options.
:::
```js config-tabs
// Preset Options: theme
// Plugin Options: @docusaurus/theme-classic
const config = {
customCss: require.resolve('./src/css/custom.css'),
};
```

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
---
sidebar_position: 3
slug: /api/themes/@docusaurus/theme-live-codeblock
---
# 📦 theme-live-codeblock
This theme provides a `@theme/CodeBlock` component that is powered by react-live. You can read more on [interactive code editor](../../guides/markdown-features/markdown-features-code-blocks.mdx#interactive-code-editor) documentation.
```bash npm2yarn
npm install --save @docusaurus/theme-live-codeblock
```
### Configuration {#configuration}
```js title="docusaurus.config.js"
module.exports = {
plugins: ['@docusaurus/theme-live-codeblock'],
themeConfig: {
liveCodeBlock: {
/**
* The position of the live playground, above or under the editor
* Possible values: "top" | "bottom"
*/
playgroundPosition: 'bottom',
},
},
};
```

View file

@ -0,0 +1,20 @@
---
sidebar_position: 4
slug: /api/themes/@docusaurus/theme-search-algolia
---
# 📦 theme-search-algolia
This theme provides a `@theme/SearchBar` component that integrates with Algolia DocSearch easily. Combined with `@docusaurus/theme-classic`, it provides a very easy search integration. You can read more on [search](../../search.mdx) documentation.
```bash npm2yarn
npm install --save @docusaurus/theme-search-algolia
```
This theme also adds search page available at `/search` (as swizzlable `SearchPage` component) path with OpenSearch support. You can change this default path via `themeConfig.algolia.searchPagePath`. Use `false` to disable search page.
:::tip
If you have installed `@docusaurus/preset-classic`, you don't need to install it as a dependency.
:::

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View file

@ -0,0 +1,658 @@
---
description: Deploy a full-featured blog in no time with Docusaurus.
---
# Blog
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
The blog feature enables you to deploy a full-featured blog in no time.
:::info
Check the [Blog Plugin API Reference documentation](./api/plugins/plugin-content-blog.mdx) for an exhaustive list of options.
:::
## Initial setup {#initial-setup}
To set up your site's blog, start by creating a `blog` directory.
Then, add an item link to your blog within `docusaurus.config.js`:
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
// ...
navbar: {
items: [
// ...
// highlight-next-line
{to: 'blog', label: 'Blog', position: 'left'}, // or position: 'right'
],
},
},
};
```
## Adding posts {#adding-posts}
To publish in the blog, create a Markdown file within the blog directory.
For example, create a file at `website/blog/2019-09-05-hello-docusaurus-v2.md`:
```md title="website/blog/2019-09-05-hello-docusaurus-v2.md"
---
title: Welcome Docusaurus v2
description: This is my first post on Docusaurus 2.
slug: welcome-docusaurus-v2
authors:
- name: Joel Marcey
title: Co-creator of Docusaurus 1
url: https://github.com/JoelMarcey
image_url: https://github.com/JoelMarcey.png
- name: Sébastien Lorber
title: Docusaurus maintainer
url: https://sebastienlorber.com
image_url: https://github.com/slorber.png
tags: [hello, docusaurus-v2]
image: https://i.imgur.com/mErPwqL.png
hide_table_of_contents: false
---
Welcome to this blog. This blog is created with [**Docusaurus 2**](https://docusaurus.io/).
<!--truncate-->
This is my first post on Docusaurus 2.
A whole bunch of exploration to follow.
```
The [front matter](./guides/markdown-features/markdown-features-intro.mdx#front-matter) is useful to add more metadata to your blog post, for example, author information, but Docusaurus will be able to infer all necessary metadata without the front matter. For all possible fields, see [the API documentation](api/plugins/plugin-content-blog.mdx#markdown-front-matter).
## Blog list {#blog-list}
The blog's index page (by default, it is at `/blog`) is the _blog list page_, where all blog posts are collectively displayed.
Use the `<!--truncate-->` marker in your blog post to represent what will be shown as the summary when viewing all published blog posts. Anything above `<!--truncate-->` will be part of the summary. For example:
```md
---
title: Truncation Example
---
All these will be part of the blog post summary.
Even this.
<!--truncate-->
But anything from here on down will not be.
Not this.
Or this.
```
By default, 10 posts are shown on each blog list page, but you can control pagination with the `postsPerPage` option in the plugin configuration. If you set `postsPerPage: 'ALL'`, pagination will be disabled and all posts will be displayed on the first page. You can also add a meta description to the blog list page for better SEO:
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-start
blogTitle: 'Docusaurus blog!',
blogDescription: 'A Docusaurus powered blog!',
postsPerPage: 'ALL',
// highlight-end
},
},
],
],
};
```
## Blog sidebar {#blog-sidebar}
The blog sidebar displays recent blog posts. The default number of items shown is 5, but you can customize with the `blogSidebarCount` option in the plugin configuration. By setting `blogSidebarCount: 0`, the sidebar will be completely disabled, with the container removed as well. This will increase the width of the main container. Specially, if you have set `blogSidebarCount: 'ALL'`, _all_ posts will be displayed.
You can also alter the sidebar heading text with the `blogSidebarTitle` option. For example, if you have set `blogSidebarCount: 'ALL'`, instead of the default "Recent posts", you may rather make it say "All posts":
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-start
blogSidebarTitle: 'All posts',
blogSidebarCount: 'ALL',
// highlight-end
},
},
],
],
};
```
## Blog post date {#blog-post-date}
Docusaurus will extract a `YYYY-MM-DD` date from a file/folder name such as `YYYY-MM-DD-my-blog-post-title.md`.
<details>
<summary>Example supported patterns</summary>
| Pattern | Example |
| --- | --- |
| Single file | `2021-05-28-my-blog-post-title.md` |
| MDX file | `2021-05-28-my-blog-post-title.mdx` |
| Single folder + `index.md` | `2021-05-28-my-blog-post-title/index.md` |
| Folder named by date | `2021-05-28/my-blog-post-title.md` |
| Nested folders by date | `2021/05/28/my-blog-post-title.md` |
| Partially nested folders by date | `2021/05-28-my-blog-post-title.md` |
| Nested folders + `index.md` | `2021/05/28/my-blog-post-title/index.md` |
| Date in the middle of path | `category/2021/05-28-my-blog-post-title.md` |
The date will be excised from the path and appended to the beginning of the URL slug.
</details>
:::tip
Using a folder can be convenient to co-locate blog post images alongside the Markdown file.
:::
This naming convention is optional, and you can also provide the date as front matter. Since the front matter follows YAML syntax where the datetime notation is supported, you can use front matter if you need more fine-grained publish dates. For example, if you have multiple posts published on the same day, you can order them according to the time of the day:
```md title="earlier-post.md"
---
date: 2021-09-13T10:00
---
```
```md title="later-post.md"
---
date: 2021-09-13T18:00
---
```
## Blog post authors {#blog-post-authors}
Use the `authors` front matter field to declare blog post authors. An author should have at least a `name` or an `image_url`. Docusaurus uses information like `url`, `email`, and `title`, but any other information is allowed.
### Inline authors {#inline-authors}
Blog post authors can be declared directly inside the front matter:
```mdx-code-block
<Tabs groupId="author-front-matter">
<TabItem value="single" label="Single author">
```
```md title="my-blog-post.md"
---
authors:
name: Joel Marcey
title: Co-creator of Docusaurus 1
url: https://github.com/JoelMarcey
image_url: https://github.com/JoelMarcey.png
email: jimarcey@gmail.com
---
```
```mdx-code-block
</TabItem>
<TabItem value="multiple" label="Multiple authors">
```
```md title="my-blog-post.md"
---
authors:
- name: Joel Marcey
title: Co-creator of Docusaurus 1
url: https://github.com/JoelMarcey
image_url: https://github.com/JoelMarcey.png
email: jimarcey@gmail.com
- name: Sébastien Lorber
title: Docusaurus maintainer
url: https://sebastienlorber.com
image_url: https://github.com/slorber.png
---
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::tip
This option works best to get started, or for casual, irregular authors.
:::
:::info
Prefer using the `authors` front matter, but the legacy `author_*` front matter remains supported:
```md title="my-blog-post.md"
---
author: Joel Marcey
author_title: Co-creator of Docusaurus 1
author_url: https://github.com/JoelMarcey
author_image_url: https://github.com/JoelMarcey.png
---
```
:::
### Global authors {#global-authors}
For regular blog post authors, it can be tedious to maintain authors' information inlined in each blog post.
It is possible to declare those authors globally in a configuration file:
```yml title="website/blog/authors.yml"
jmarcey:
name: Joel Marcey
title: Co-creator of Docusaurus 1
url: https://github.com/JoelMarcey
image_url: https://github.com/JoelMarcey.png
email: jimarcey@gmail.com
slorber:
name: Sébastien Lorber
title: Docusaurus maintainer
url: https://sebastienlorber.com
image_url: https://github.com/slorber.png
```
:::tip
Use the `authorsMapPath` plugin option to configure the path. JSON is also supported.
:::
In blog posts front matter, you can reference the authors declared in the global configuration file:
```mdx-code-block
<Tabs groupId="author-front-matter">
<TabItem value="single" label="Single author">
```
```md title="my-blog-post.md"
---
authors: jmarcey
---
```
```mdx-code-block
</TabItem>
<TabItem value="multiple" label="Multiple authors">
```
```md title="my-blog-post.md"
---
authors: [jmarcey, slorber]
---
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::info
The `authors` system is very flexible and can suit more advanced use-case:
<details>
<summary>Mix inline authors and global authors</summary>
You can use global authors most of the time, and still use inline authors:
```md title="my-blog-post.md"
---
authors:
- jmarcey
- slorber
- name: Inline Author name
title: Inline Author Title
url: https://github.com/inlineAuthor
image_url: https://github.com/inlineAuthor
---
```
</details>
<details>
<summary>Local override of global authors</summary>
You can customize the global author's data on per-blog-post basis:
```md title="my-blog-post.md"
---
authors:
- key: jmarcey
title: Joel Marcey's new title
- key: slorber
name: Sébastien Lorber's new name
---
```
</details>
<details>
<summary>Localize the author's configuration file</summary>
The configuration file can be localized, just create a localized copy of it at:
```bash
website/i18n/[locale]/docusaurus-plugin-content-blog/authors.yml
```
</details>
:::
An author, either declared through front matter or through the authors map, needs to have a name or an avatar, or both. If all authors of a post don't have names, Docusaurus will display their avatars compactly. See [this test post](/tests/blog/2022/01/20/image-only-authors) for the effect.
:::caution Feed generation
[RSS feeds](#feed) require the author's email to be set for the author to appear in the feed.
:::
## Reading time {#reading-time}
Docusaurus generates a reading time estimation for each blog post based on word count. We provide an option to customize this.
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-start
showReadingTime: true, // When set to false, the "x min read" won't be shown
readingTime: ({content, frontMatter, defaultReadingTime}) =>
defaultReadingTime({content, options: {wordsPerMinute: 300}}),
// highlight-end
},
},
],
],
};
```
The `readingTime` callback receives three parameters: the blog content text as a string, front matter as a record of string keys and their values, and the default reading time function. It returns a number (reading time in minutes) or `undefined` (disable reading time for this page).
The default reading time is able to accept additional options: `wordsPerMinute` as a number (default: 300), and `wordBound` as a function from string to boolean. If the string passed to `wordBound` should be a word bound (spaces, tabs, and line breaks by default), the function should return `true`.
:::tip
Use the callback for all your customization needs:
```mdx-code-block
<Tabs>
<TabItem value="disable-per-post" label="Per-post disabling">
```
**Disable reading time on one page:**
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
showReadingTime: true,
// highlight-start
readingTime: ({content, frontMatter, defaultReadingTime}) =>
frontMatter.hide_reading_time
? undefined
: defaultReadingTime({content}),
// highlight-end
},
},
],
],
};
```
Usage:
```md "my-blog-post.md"
---
hide_reading_time: true
---
This page will no longer display the reading time stats!
```
```mdx-code-block
</TabItem>
<TabItem value="passing-options" label="Passing options">
```
**Pass options to the default reading time function:**
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-start
readingTime: ({content, defaultReadingTime}) =>
defaultReadingTime({content, options: {wordsPerMinute: 100}}),
// highlight-end
},
},
],
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="using-custom-algo" label="Using custom algorithms">
```
**Use a custom implementation of reading time:**
```js title="docusaurus.config.js"
const myReadingTime = require('./myReadingTime');
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-next-line
readingTime: ({content}) => myReadingTime(content),
},
},
],
],
};
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::
## Feed {#feed}
You can generate RSS / Atom / JSON feed by passing `feedOptions`. By default, RSS and Atom feeds are generated. To disable feed generation, set `feedOptions.type` to `null`.
```ts
type FeedType = 'rss' | 'atom' | 'json';
type BlogOptions = {
feedOptions?: {
type?: FeedType | 'all' | FeedType[] | null;
title?: string;
description?: string;
copyright: string;
language?: string; // possible values: http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
/** Allow control over the construction of BlogFeedItems */
createFeedItems?: (params: {
blogPosts: BlogPost[];
siteConfig: DocusaurusConfig;
outDir: string;
defaultCreateFeedItems: (params: {
blogPosts: BlogPost[];
siteConfig: DocusaurusConfig;
outDir: string;
}) => Promise<BlogFeedItem[]>;
}) => Promise<BlogFeedItem[]>;
};
};
```
Example usage:
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
// highlight-start
feedOptions: {
type: 'all',
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`,
createFeedItems: async (params) => {
const {blogPosts, defaultCreateFeedItems, ...rest} = params;
return defaultCreateFeedItems({
// keep only the 10 most recent blog posts in the feed
blogPosts: blogPosts.filter((item, index) => index < 10),
...rest,
});
},
},
// highlight-end
},
},
],
],
};
```
The feeds can be found at:
<Tabs>
<TabItem value="RSS">
```text
https://example.com/blog/rss.xml
```
</TabItem>
<TabItem value="Atom">
```text
https://example.com/blog/atom.xml
```
</TabItem>
<TabItem value="JSON">
```text
https://example.com/blog/feed.json
```
</TabItem>
</Tabs>
## Advanced topics {#advanced-topics}
### Blog-only mode {#blog-only-mode}
You can run your Docusaurus 2 site without a dedicated landing page and instead have your blog's post list page as the index page. Set the `routeBasePath` to be `'/'` to serve the blog through the root route `example.com/` instead of the subroute `example.com/blog/`.
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
// highlight-next-line
docs: false, // Optional: disable the docs plugin
blog: {
// highlight-next-line
routeBasePath: '/', // Serve the blog at the site's root
/* other blog options */
},
},
],
],
};
```
:::caution
Don't forget to delete the existing homepage at `./src/pages/index.js` or else there will be two files mapping to the same route!
:::
:::tip
There's also a "Docs-only mode" for those who only want to use the docs. Read [Docs-only mode](./guides/docs/docs-introduction.mdx) for detailed instructions or a more elaborate explanation of `routeBasePath`.
:::
### Multiple blogs {#multiple-blogs}
By default, the classic theme assumes only one blog per website and hence includes only one instance of the blog plugin. If you would like to have multiple blogs on a single website, it's possible too! You can add another blog by specifying another blog plugin in the `plugins` option for `docusaurus.config.js`.
Set the `routeBasePath` to the URL route that you want your second blog to be accessed on. Note that the `routeBasePath` here has to be different from the first blog or else there could be a collision of paths! Also, set `path` to the path to the directory containing your second blog's entries.
As documented for [multi-instance plugins](./using-plugins.mdx#multi-instance-plugins-and-plugin-ids), you need to assign a unique ID to the plugins.
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
[
'@docusaurus/plugin-content-blog',
{
/**
* Required for any multi-instance plugin
*/
id: 'second-blog',
/**
* URL route for the blog section of your site.
* *DO NOT* include a trailing slash.
*/
routeBasePath: 'my-second-blog',
/**
* Path to data on filesystem relative to site dir.
*/
path: './my-second-blog',
},
],
],
};
```
As an example, we host a second blog [here](/tests/blog).

View file

@ -0,0 +1,106 @@
---
description: How to keep a reasonable bundle size while ensuring sufficient browser support.
---
# Browser support
Docusaurus allows sites to define the list of supported browsers through a [browserslist configuration](https://github.com/browserslist/browserslist).
## Purpose {#purpose}
Websites need to balance between backward compatibility and bundle size. As old browsers do not support modern APIs or syntax, more code is needed to implement the same functionality.
For example, you may use the [optional chaining syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining):
```js
const value = obj?.prop?.val;
```
...which unfortunately is only recognized by browser versions released after 2020. To be compatible with earlier browser versions, when building your site for production, our JS loader will transpile your code to a more verbose syntax:
```js
var _obj, _obj$prop;
const value =
(_obj = obj) === null || _obj === void 0
? void 0
: (_obj$prop = _obj.prop) === null || _obj$prop === void 0
? void 0
: _obj$prop.val;
```
However, this penalizes all other users with increased site load time because the 29-character line now becomes 168 characters—a 6-fold increase! (In practice, it will be better because the names used will be shorter.) As a tradeoff, the JS loader only transpiles the syntax to the degree that's supported by all browser versions defined in the browser list.
The browser list by default is provided through the `package.json` file as a root `browserslist` field.
:::caution
On old browsers, the compiled output will use unsupported (too recent) JS syntax, causing React to fail to initialize and end up with a static website with only HTML/CSS and no JS.
:::
## Default values {#default-values}
Websites initialized with the default classic template has the following in `package.json`:
```json title="package.json"
{
"name": "docusaurus",
// ...
// highlight-start
"browserslist": {
"production": [">0.5%", "not dead", "not op_mini all"],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
// highlight-end
// ...
}
```
Explained in natural language, the browsers supported in production are those:
- With more than 0.5% of market share; _and_
- Has official support or updates in the past 24 months (the opposite of "dead"); _and_
- Is not Opera Mini.
And browsers used in development are:
- The latest version of Chrome _or_ Firefox _or_ Safari.
You can "evaluate" any config with the `browserslist` CLI to obtain the actual list:
```bash
npx browserslist --env="production"
```
The output is all browsers supported in production. Below is the output in January 2022:
```text
and_chr 96
and_uc 12.12
chrome 96
chrome 95
chrome 94
edge 96
firefox 95
firefox 94
ie 11
ios_saf 15.2
ios_saf 15.0-15.1
ios_saf 14.5-14.8
ios_saf 14.0-14.4
ios_saf 12.2-12.5
opera 82
opera 81
safari 15.1
safari 14.1
safari 13.1
```
## Read more {#read-more}
You may wish to visit the [browserslist documentation](https://github.com/browserslist/browserslist/blob/main/README.md) for more specifications, especially the accepted [query values](https://github.com/browserslist/browserslist/blob/main/README.md#queries) and [best practices](https://github.com/browserslist/browserslist/blob/main/README.md#best-practices).

View file

@ -0,0 +1,185 @@
---
description: Docusaurus provides a set of scripts to help you generate, serve, and deploy your website.
---
# CLI
Docusaurus provides a set of scripts to help you generate, serve, and deploy your website.
Once your website is bootstrapped, the website source will contain the Docusaurus scripts that you can invoke with your package manager:
```json title="package.json"
{
// ...
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
}
}
```
## Docusaurus CLI commands {#docusaurus-cli-commands}
Below is a list of Docusaurus CLI commands and their usages:
### `docusaurus start [siteDir]` {#docusaurus-start-sitedir}
Builds and serves a preview of your site locally with [Webpack Dev Server](https://webpack.js.org/configuration/dev-server).
#### Options {#options}
| Name | Default | Description |
| --- | --- | --- |
| `--port` | `3000` | Specifies the port of the dev server. |
| `--host` | `localhost` | Specify a host to use. For example, if you want your server to be accessible externally, you can use `--host 0.0.0.0`. |
| `--hot-only` | `false` | Enables Hot Module Replacement without page refresh as a fallback in case of build failures. More information [here](https://webpack.js.org/configuration/dev-server/#devserverhotonly). |
| `--no-open` | `false` | Do not open automatically the page in the browser. |
| `--config` | `undefined` | Path to Docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
| `--poll [optionalIntervalMs]` | `false` | Use polling of files rather than watching for live reload as a fallback in environments where watching doesn't work. More information [here](https://webpack.js.org/configuration/watch/#watchoptionspoll). |
| `--no-minify` | `false` | Build website without minimizing JS/CSS bundles. |
:::important
Please note that some functionality (for example, anchor links) will not work in development. The functionality will work as expected in production.
:::
:::info Development over network
When forwarding port 3000 from a remote server or VM (e.g. GitHub Codespaces), you can run the dev server on `0.0.0.0` to make it listen on the local IP.
```bash npm2yarn
npm run start -- --host 0.0.0.0
```
:::
#### Enabling HTTPS {#enabling-https}
There are multiple ways to obtain a certificate. We will use [mkcert](https://github.com/FiloSottile/mkcert) as an example.
1. Run `mkcert localhost` to generate `localhost.pem` + `localhost-key.pem`
2. Run `mkcert -install` to install the cert in your trust store, and restart your browser
3. Start the app with Docusaurus HTTPS env variables:
```bash
HTTPS=true SSL_CRT_FILE=localhost.pem SSL_KEY_FILE=localhost-key.pem yarn start
```
4. Open `https://localhost:3000/`
### `docusaurus build [siteDir]` {#docusaurus-build-sitedir}
Compiles your site for production.
#### Options {#options-1}
| Name | Default | Description |
| --- | --- | --- |
| `--bundle-analyzer` | `false` | Analyze your bundle with the [webpack bundle analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer). |
| `--out-dir` | `build` | The full path for the new output directory, relative to the current workspace. |
| `--config` | `undefined` | Path to Docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
| `--no-minify` | `false` | Build website without minimizing JS/CSS bundles. |
:::info
For advanced minification of CSS bundle, we use the [advanced cssnano preset](https://github.com/cssnano/cssnano/tree/master/packages/cssnano-preset-advanced) (along with additional several PostCSS plugins) and [level 2 optimization of clean-css](https://github.com/jakubpawlowicz/clean-css#level-2-optimizations). If as a result of this advanced CSS minification you find broken CSS, build your website with the environment variable `USE_SIMPLE_CSS_MINIFIER=true` to minify CSS with the [default cssnano preset](https://github.com/cssnano/cssnano/tree/master/packages/cssnano-preset-default). **Please [fill out an issue](https://github.com/facebook/docusaurus/issues/new?labels=bug%2C+needs+triage&template=bug.md) if you experience CSS minification bugs.**
You can skip the HTML minification with the environment variable `SKIP_HTML_MINIFICATION=true`.
:::
### `docusaurus swizzle [themeName] [componentName] [siteDir]` {#docusaurus-swizzle}
[Swizzle](./swizzling.mdx) a theme component to customize it.
```bash npm2yarn
npm run swizzle [themeName] [componentName] [siteDir]
# Example (leaving out the siteDir to indicate this directory)
npm run swizzle @docusaurus/theme-classic Footer -- --eject
```
The swizzle CLI is interactive and will guide you through the whole [swizzle process](./swizzling.mdx).
#### Options {#options-swizzle}
| Name | Description |
| --- | --- |
| `themeName` | The name of the theme to swizzle from. |
| `componentName` | The name of the theme component to swizzle. |
| `--list` | Display components available for swizzling |
| `--eject` | [Eject](./swizzling.mdx#ejecting) the theme component |
| `--wrap` | [Wrap](./swizzling.mdx#wrapping) the theme component |
| `--danger` | Allow immediate swizzling of unsafe components |
| `--typescript` | Swizzle the TypeScript variant component |
| `--config` | Path to docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
:::caution
Unsafe components have a higher risk of breaking changes due to internal refactorings.
:::
### `docusaurus deploy [siteDir]` {#docusaurus-deploy-sitedir}
Deploys your site with [GitHub Pages](https://pages.github.com/). Check out the docs on [deployment](deployment.mdx#deploying-to-github-pages) for more details.
#### Options {#options-3}
| Name | Default | Description |
| --- | --- | --- |
| `--out-dir` | `build` | The full path for the new output directory, relative to the current workspace. |
| `--skip-build` | `false` | Deploy website without building it. This may be useful when using a custom deploy script. |
| `--config` | `undefined` | Path to Docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
### `docusaurus serve [siteDir]` {#docusaurus-serve-sitedir}
Serve your built website locally.
| Name | Default | Description |
| --- | --- | --- |
| `--port` | `3000` | Use specified port |
| `--dir` | `build` | The full path for the output directory, relative to the current workspace |
| `--build` | `false` | Build website before serving |
| `--config` | `undefined` | Path to Docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
| `--host` | `localhost` | Specify a host to use. For example, if you want your server to be accessible externally, you can use `--host 0.0.0.0`. |
| `--no-open` | `false` locally, `true` in CI | Do not open a browser window to the server location. |
### `docusaurus clear [siteDir]` {#docusaurus-clear-sitedir}
Clear a Docusaurus site's generated assets, caches, build artifacts.
We recommend running this command before reporting bugs, after upgrading versions, or anytime you have issues with your Docusaurus site.
### `docusaurus write-translations [siteDir]` {#docusaurus-write-translations-sitedir}
Write the JSON translation files that you will have to translate.
By default, the files are written in `website/i18n/<defaultLocale>/...`.
| Name | Default | Description |
| --- | --- | --- |
| `--locale` | `<defaultLocale>` | Define which locale folder you want to write translations the JSON files in |
| `--override` | `false` | Override existing translation messages |
| `--config` | `undefined` | Path to Docusaurus config file, default to `[siteDir]/docusaurus.config.js` |
| `--messagePrefix` | `''` | Allows adding a prefix to each translation message, to help you highlight untranslated strings |
### `docusaurus write-heading-ids [siteDir] [files]` {#docusaurus-write-heading-ids-sitedir}
Add [explicit heading IDs](./guides/markdown-features/markdown-features-toc.mdx#explicit-ids) to the Markdown documents of your site.
| Name | Default | Description |
| --- | --- | --- |
| `files` | All MD files used by plugins | The files that you want heading IDs to be written to. |
| `--maintain-case` | `false` | Keep the headings' casing, otherwise make all lowercase. |
| `--overwrite` | `false` | Overwrite existing heading IDs. |

View file

@ -0,0 +1,182 @@
---
description: Configuring your site's behavior through docusaurus.config.js and more.
---
# Configuration
import TOCInline from '@theme/TOCInline';
Docusaurus has a unique take on configurations. We encourage you to congregate information about your site into one place. We guard the fields of this file and facilitate making this data object accessible across your site.
Keeping a well-maintained `docusaurus.config.js` helps you, your collaborators, and your open source contributors to be able to focus on documentation while still being able to customize the site.
## What goes into a `docusaurus.config.js`? {#what-goes-into-a-docusaurusconfigjs}
You should not have to write your `docusaurus.config.js` from scratch even if you are developing your site. All templates come with a `docusaurus.config.js` that includes defaults for the common options.
However, it can be helpful if you have a high-level understanding of how the configurations are designed and implemented.
The high-level overview of Docusaurus configuration can be categorized into:
<TOCInline toc={toc} minHeadingLevel={3} maxHeadingLevel={3} />
For exact reference to each of the configurable fields, you may refer to [**`docusaurus.config.js` API reference**](api/docusaurus.config.js.mdx).
### Site metadata {#site-metadata}
Site metadata contains the essential global metadata such as `title`, `url`, `baseUrl`, and `favicon`.
They are used in several places such as your site's title and headings, browser tab icon, social sharing (Facebook, Twitter) information or even to generate the correct path to serve your static files.
### Deployment configurations {#deployment-configurations}
Deployment configurations such as `projectName`, `organizationName`, and optionally `deploymentBranch` are used when you deploy your site with the `deploy` command.
It is recommended to check the [deployment docs](deployment.mdx) for more information.
### Theme, plugin, and preset configurations {#theme-plugin-and-preset-configurations}
List the [themes](./using-plugins.mdx#using-themes), [plugins](./using-plugins.mdx), and [presets](./using-plugins.mdx#using-presets) for your site in the `themes`, `plugins`, and `presets` fields, respectively. These are typically npm packages:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
'@docusaurus/plugin-content-blog',
'@docusaurus/plugin-content-pages',
],
themes: ['@docusaurus/theme-classic'],
};
```
:::tip
Docusaurus supports [**module shorthands**](./using-plugins.mdx#module-shorthands), allowing you to simplify the above configuration as:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: ['content-blog', 'content-pages'],
themes: ['classic'],
};
```
:::
They can also be loaded from local directories:
```js title="docusaurus.config.js"
const path = require('path');
module.exports = {
// ...
themes: [path.resolve(__dirname, '/path/to/docusaurus-local-theme')],
};
```
To specify options for a plugin or theme, replace the name of the plugin or theme in the config file with an array containing the name and an options object:
```js title="docusaurus.config.js"
module.exports = {
// ...
plugins: [
[
'content-blog',
{
path: 'blog',
routeBasePath: 'blog',
include: ['*.md', '*.mdx'],
// ...
},
],
'content-pages',
],
};
```
To specify options for a plugin or theme that is bundled in a preset, pass the options through the `presets` field. In this example, `docs` refers to `@docusaurus/plugin-content-docs` and `theme` refers to `@docusaurus/theme-classic`.
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
},
theme: {
customCss: [require.resolve('./src/css/custom.css')],
},
},
],
],
};
```
:::tip
The `presets: [['classic', {...}]]` shorthand works as well.
:::
For further help configuring themes, plugins, and presets, see [Using Plugins](./using-plugins.mdx).
### Custom configurations {#custom-configurations}
Docusaurus guards `docusaurus.config.js` from unknown fields. To add custom fields, define them in `customFields`.
Example:
```js title="docusaurus.config.js"
module.exports = {
// ...
// highlight-start
customFields: {
image: '',
keywords: [],
},
// highlight-end
// ...
};
```
## Accessing configuration from components {#accessing-configuration-from-components}
Your configuration object will be made available to all the components of your site. And you may access them via React context as `siteConfig`.
Basic example:
```jsx
import React from 'react';
// highlight-next-line
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
const Hello = () => {
// highlight-start
const {siteConfig} = useDocusaurusContext();
// highlight-end
const {title, tagline} = siteConfig;
return <div>{`${title} · ${tagline}`}</div>;
};
```
:::tip
If you just want to use those fields on the client side, you could create your own JS files and import them as ES6 modules, there is no need to put them in `docusaurus.config.js`.
:::
## Customizing Babel Configuration {#customizing-babel-configuration}
For new Docusaurus projects, we automatically generated a `babel.config.js` in the project root.
```js title="babel.config.js"
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
```
Most of the time, this configuration will work just fine. If you want to customize your Babel configuration (e.g. to add support for Flow), you can directly edit this file. For your changes to take effect, you need to restart the Docusaurus dev server.

View file

@ -0,0 +1,809 @@
---
description: Deploy your Docusaurus app for production on a range of static site hosting services.
---
# Deployment
To build the static files of your website for production, run:
```bash npm2yarn
npm run build
```
Once it finishes, the static files will be generated within the `build` directory.
:::note
The only responsibility of Docusaurus is to build your site and emit static files in `build`.
It is now up to you to choose how to host those static files.
:::
You can deploy your site to static site hosting services such as [Vercel](https://vercel.com/), [GitHub Pages](https://pages.github.com/), [Netlify](https://www.netlify.com/), [Render](https://render.com/docs/static-sites), [Surge](https://surge.sh/help/getting-started-with-surge)...
A Docusaurus site is statically rendered, and it can generally work without JavaScript!
## Configuration {#configuration}
The following parameters are required in `docusaurus.config.js` to optimize routing and serve files from the correct location:
| Name | Description |
| --- | --- |
| `url` | URL for your site. For a site deployed at `https://my-org.com/my-project/`, `url` is `https://my-org.com/`. |
| `baseUrl` | Base URL for your project, with a trailing slash. For a site deployed at `https://my-org.com/my-project/`, `baseUrl` is `/my-project/`. |
## Testing your Build Locally {#testing-build-locally}
It is important to test your build locally before deploying it for production. Docusaurus provides a [`docusaurus serve`](cli.mdx#docusaurus-serve-sitedir) command for that:
```bash npm2yarn
npm run serve
```
By default, this will load your site at [http://localhost:3000/](http://localhost:3000/).
## Trailing slash configuration {#trailing-slashes}
Docusaurus has a [`trailingSlash` config](./api/docusaurus.config.js.mdx#trailingSlash), to allow customizing URLs/links and emitted filename patterns.
The default value generally works fine. Unfortunately, each static hosting provider has a **different behavior**, and deploying the exact same site to various hosts can lead to distinct results. Depending on your host, it can be useful to change this config.
:::tip
Use [slorber/trailing-slash-guide](https://github.com/slorber/trailing-slash-guide) to understand better the behavior of your host and configure `trailingSlash` appropriately.
:::
## Using environment variables {#using-environment-variables}
Putting potentially sensitive information in the environment is common practice. However, in a typical Docusaurus website, the `docusaurus.config.js` file is the only interface to the Node.js environment (see [our architecture overview](advanced/architecture.mdx)), while everything else—MDX pages, React components... are client side and do not have direct access to the `process` global. In this case, you can consider using [`customFields`](api/docusaurus.config.js.mdx#customFields) to pass environment variables to the client side.
```js title="docusaurus.config.js"
// If you are using dotenv (https://www.npmjs.com/package/dotenv)
require('dotenv').config();
module.exports = {
title: '...',
url: process.env.URL, // You can use environment variables to control site specifics as well
// highlight-start
customFields: {
// Put your custom environment here
teamEmail: process.env.EMAIL,
},
// highlight-end
};
```
```jsx title="home.jsx"
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
export default function Home() {
const {
siteConfig: {customFields},
} = useDocusaurusContext();
return <div>Contact us through {customFields.teamEmail}!</div>;
}
```
## Choosing a hosting provider {#choosing-a-hosting-provider}
There are a few common hosting options:
- [Self hosting](#self-hosting) with an HTTP server like Apache2 or Nginx;
- Jamstack providers, e.g. [Netlify](#deploying-to-netlify) and [Vercel](#deploying-to-vercel). We will use them as references, but the same reasoning can apply to other providers.
- [GitHub Pages](#deploying-to-github-pages). (By definition, it is also Jamstack, but we compare it separately.)
If you are unsure of which one to choose, ask the following questions:
<details>
<summary>
How much resource (person-hours, money) am I willing to invest in this?
</summary>
- 🔴 Self-hosting is the hardest to set up—you would usually need an experienced person to manage this. Cloud services are almost never free, and setting up an on-site server and connecting it to the WAN can be even more costly.
- 🟢 Jamstack providers can help you set up a working website in almost no time and offers features like server-side redirects that are easily configurable. Many providers offer generous build time quotas even for free plans that you would almost never exceed. However, it's still ultimately limited—you would need to pay once you hit the limit. Check the pricing page of your provider for details.
- 🟡 The GitHub Pages deployment workflow can be tedious to set up. (Evidence: see the length of [Deploying to GitHub Pages](#deploying-to-github-pages)!) However, this service (including build and deployment) is always free for public repositories, and we have detailed instructions to help you make it work.
</details>
<details>
<summary>How much server-side configuration would I need?</summary>
- 🟢 With self-hosting, you have access to the entire server's configuration. You can configure the virtual host to serve different content based on the request URL; you can do complicated server-side redirects; you can put part of the site behind authentication... If you need a lot of server-side features, self-host your website.
- 🟡 Jamstack usually offers some server-side configurations, e.g. URLs formatting (trailing slashes), server-side redirects...
- 🔴 GitHub Pages doesn't expose server-side configurations besides enforcing HTTPS and setting CNAME.
</details>
<details>
<summary>Do I have needs to cooperate?</summary>
- 🟡 Self-hosted services can achieve the same effect as Netlify, but with much more heavy-lifting. Usually, you would have a specific person who looks after the deployment, and the workflow won't be very git-based as opposed to the other two options.
- 🟢 Netlify and Vercel have deploy previews for every pull request, which is useful for a team to review work before merging to production. You can also manage a team with different member access to the deployment.
- 🟡 GitHub Pages cannot do deploy previews in a non-convoluted way. One repo can only be associated with one site deployment. On the other hand, you can control who has write access to the site's deployment.
</details>
There isn't a silver bullet. You need to weigh your needs and resources before making a choice.
## Self-Hosting {#self-hosting}
Docusaurus can be self-hosted using [`docusaurus serve`](cli.mdx#docusaurus-serve-sitedir). Change port using `--port` and `--host` to change host.
```bash npm2yarn
npm run serve -- --build --port 80 --host 0.0.0.0
```
:::warning
It is not the best option, compared to a static hosting provider / CDN.
:::
:::warning
In the following sections, we will introduce a few common hosting providers and how they should be configured to deploy Docusaurus sites most efficiently. Some of the writeups are provided by external contributors. Docusaurus is not interest-related with any of the services. The documentation may not be up-to-date: recent changes in their API may not be reflected on our side. If you see outdated content, PRs are welcome.
For the same concern of up-to-datedness, we have stopped accepting PRs adding new hosting options. You can, however, publish your writeup on a separate site (e.g. your blog, or the provider's official website), and ask us to include a link to your writeup.
:::
## Deploying to Netlify {#deploying-to-netlify}
To deploy your Docusaurus 2 sites to [Netlify](https://www.netlify.com/), first make sure the following options are properly configured:
```js title="docusaurus.config.js"
module.exports = {
// highlight-start
url: 'https://docusaurus-2.netlify.app', // Url to your site with no trailing slash
baseUrl: '/', // Base directory of your site relative to your repo
// highlight-end
// ...
};
```
Then, [create your site with Netlify](https://app.netlify.com/start).
While you set up the site, specify the build commands and directories as follows:
- build command: `npm run build`
- publish directory: `build`
If you did not configure these build options, you may still go to "Site settings" -> "Build & deploy" after your site is created.
Once properly configured with the above options, your site should deploy and automatically redeploy upon merging to your deploy branch, which defaults to `main`.
:::caution
Some Docusaurus sites put the `docs` folder outside of `website` (most likely former Docusaurus v1 sites):
```bash
repo # git root
├── docs # MD files
└── website # Docusaurus root
```
If you decide to use the `website` folder as Netlify's base directory, Netlify will not trigger builds when you update the `docs` folder, and you need to configure a [custom `ignore` command](https://docs.netlify.com/configure-builds/common-configurations/ignore-builds/):
```toml title="website/netlify.toml"
[build]
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../docs/"
```
:::
:::warning
By default, Netlify adds trailing slashes to Docusaurus URLs.
It is recommended to disable the Netlify setting `Post Processing > Asset Optimization > Pretty Urls` to prevent lowercased URLs, unnecessary redirects, and 404 errors.
**Be very careful**: the `Disable asset optimization` global checkbox is broken and does not really disable the `Pretty URLs` setting in practice. Please make sure to **uncheck it independently**.
If you want to keep the `Pretty Urls` Netlify setting on, adjust the `trailingSlash` Docusaurus config appropriately.
Refer to [slorber/trailing-slash-guide](https://github.com/slorber/trailing-slash-guide) for more information.
:::
## Deploying to Vercel {#deploying-to-vercel}
Deploying your Docusaurus project to [Vercel](https://vercel.com/) will provide you with [various benefits](https://vercel.com/) in the areas of performance and ease of use.
To deploy your Docusaurus project with a [Vercel for Git Integration](https://vercel.com/docs/concepts/git), make sure it has been pushed to a Git repository.
Import the project into Vercel using the [Import Flow](https://vercel.com/import/git). During the import, you will find all relevant options preconfigured for you; however, you can choose to change any of these options, a list of which can be found [here](https://vercel.com/docs/build-step#build-&-development-settings).
After your project has been imported, all subsequent pushes to branches will generate [Preview Deployments](https://vercel.com/docs/platform/deployments#preview), and all changes made to the [Production Branch](https://vercel.com/docs/git-integrations#production-branch) (usually "main" or "master") will result in a [Production Deployment](https://vercel.com/docs/platform/deployments#production).
## Deploying to GitHub Pages {#deploying-to-github-pages}
Docusaurus provides an easy way to publish to [GitHub Pages](https://pages.github.com/), which comes for free with every GitHub repository.
### Overview {#github-pages-overview}
Usually, there are two repositories (at least, two branches) involved in a publishing process: the branch containing the source files, and the branch containing the build output to be served with GitHub Pages. In the following tutorial, they will be referred to as **"source"** and **"deployment"**, respectively.
Each GitHub repository is associated with a GitHub Pages service. If the deployment repository is called `my-org/my-project` (where `my-org` is the organization name or username), the deployed site will appear at `https://my-org.github.io/my-project/`. Specially, if the deployment repository is called `my-org/my-org.github.io` (the _organization GitHub Pages repo_), the site will appear at `https://my-org.github.io/`.
:::info
In case you want to use your custom domain for GitHub Pages, create a `CNAME` file in the `static` directory. Anything within the `static` directory will be copied to the root of the `build` directory for deployment. When using a custom domain, you should be able to move back from `baseUrl: '/projectName/'` to `baseUrl: '/'`, and also set your `url` to your custom domain.
You may refer to GitHub Pages' documentation [User, Organization, and Project Pages](https://help.github.com/en/articles/user-organization-and-project-pages) for more details.
:::
GitHub Pages picks up deploy-ready files (the output from `docusaurus build`) from the default branch (`master` / `main`, usually) or the `gh-pages` branch, and either from the root or the `/docs` folder. You can configure that through `Settings > Pages` in your repository. This branch will be called the "deployment branch".
We provide a `docusaurus deploy` command that helps you deploy your site from the source branch to the deployment branch in one command: clone, build, and commit.
### `docusaurus.config.js` settings {#docusaurusconfigjs-settings}
First, modify your `docusaurus.config.js` and add the following params:
| Name | Description |
| --- | --- |
| `organizationName` | The GitHub user or organization that owns the deployment repository. |
| `projectName` | The name of the deployment repository. |
| `deploymentBranch` | The name of deployment branch. Defaults to `'gh-pages'` for non-organization GitHub Pages repos (`projectName` not ending in `.github.io`). Otherwise, this needs to be explicit as a config field or environment variable. |
These fields also have their environment variable counterparts, which have a higher priority: `ORGANIZATION_NAME`, `PROJECT_NAME`, and `DEPLOYMENT_BRANCH`.
:::caution
GitHub Pages adds a trailing slash to Docusaurus URLs by default. It is recommended to set a `trailingSlash` config (`true` or `false`, not `undefined`).
:::
Example:
```js title="docusaurus.config.js"
module.exports = {
// ...
url: 'https://endiliey.github.io', // Your website URL
baseUrl: '/',
// highlight-start
projectName: 'endiliey.github.io',
organizationName: 'endiliey',
trailingSlash: false,
// highlight-end
// ...
};
```
:::warning
By default, GitHub Pages runs published files through [Jekyll](https://jekyllrb.com/). Since Jekyll will discard any files that begin with `_`, it is recommended that you disable Jekyll by adding an empty file named `.nojekyll` file to your `static` directory.
:::
### Environment settings {#environment-settings}
| Name | Description |
| --- | --- |
| `USE_SSH` | Set to `true` to use SSH instead of the default HTTPS for the connection to the GitHub repo. If the source repo URL is an SSH URL (e.g. `git@github.com:facebook/docusaurus.git`), `USE_SSH` is inferred to be `true`. |
| `GIT_USER` | The username for a GitHub account that **has push access to the deployment repo**. For your own repositories, this will usually be your GitHub username. Required if not using SSH, and ignored otherwise. |
| `GIT_PASS` | Personal access token of the git user (specified by `GIT_USER`), to facilitate non-interactive deployment (e.g. continuous deployment) |
| `CURRENT_BRANCH` | The source branch. Usually, the branch will be `main` or `master`, but it could be any branch except for `gh-pages`. If nothing is set for this variable, then the current branch from which `docusaurus deploy` is invoked will be used. |
GitHub enterprise installations should work in the same manner as github.com; you only need to set the organization's GitHub Enterprise host as an environment variable:
| Name | Description |
| ------------- | ----------------------------------------------- |
| `GITHUB_HOST` | The domain name of your GitHub enterprise site. |
| `GITHUB_PORT` | The port of your GitHub enterprise site. |
### Deploy {#deploy}
Finally, to deploy your site to GitHub Pages, run:
```mdx-code-block
<Tabs>
<TabItem value="bash" label="Bash">
```
```bash
GIT_USER=<GITHUB_USERNAME> yarn deploy
```
```mdx-code-block
</TabItem>
<TabItem value="windows" label="Windows">
```
```batch
cmd /C "set "GIT_USER=<GITHUB_USERNAME>" && yarn deploy"
```
```mdx-code-block
</TabItem>
<TabItem value="powershell" label="PowerShell">
```
```powershell
cmd /C 'set "GIT_USER=<GITHUB_USERNAME>" && yarn deploy'
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::caution
Beginning in August 2021, GitHub requires every command-line sign-in to use the **personal access token** instead of the password. When GitHub prompts for your password, enter the PAT instead. See the [GitHub documentation](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) for more information.
Alternatively, you can use SSH (`USE_SSH=true`) to log in.
:::
### Triggering deployment with GitHub Actions {#triggering-deployment-with-github-actions}
[GitHub Actions](https://help.github.com/en/actions) allow you to automate, customize, and execute your software development workflows right in your repository.
The workflow examples below assume your website source resides in the `main` branch of your repository (the _source branch_ is `main`), and your [publishing source](https://help.github.com/en/github/working-with-github-pages/configuring-a-publishing-source-for-your-github-pages-site) is configured for the `gh-pages` branch (the _deployment branch_ is `gh-pages`).
Our goal is that:
1. When a new pull request is made to `main`, there's an action that ensures the site builds successfully, without actually deploying. This job will be called `test-deploy`.
2. When a pull request is merged to the `main` branch or someone pushes to the `main` branch directly, it will be built and deployed to the `gh-pages` branch. After that, the new build output will be served on the GitHub Pages site. This job will be called `deploy`.
Here are two approaches to deploying your docs with GitHub Actions. Based on the location of your deployment branch (`gh-pages`), choose the relevant tab below:
- Source repo and deployment repo are the **same** repository.
- The deployment repo is a **remote** repository, different from the source.
```mdx-code-block
<Tabs>
<TabItem value="same" label="Same">
```
While you can have both jobs defined in the same workflow file, the original `deploy` workflow will always be listed as skipped in the PR check suite status, which is not communicative of the actual status and provides no value to the review process. We therefore propose to manage them as separate workflows instead.
We will use a popular third-party deployment action: [peaceiris/actions-gh-pages](https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus).
<details>
<summary>GitHub action files</summary>
Add these two workflow files:
:::warning Tweak the parameters for your setup
These files assume you are using Yarn. If you use npm, change `cache: yarn`, `yarn install --frozen-lockfile`, `yarn build` to `cache: npm`, `npm ci`, `npm run build` accordingly.
If your Docusaurus project is not at the root of your repo, you may need to configure a [default working directory](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-set-the-default-shell-and-working-directory), and adjust the paths accordingly.
:::
```yml title=".github/workflows/deploy.yml"
name: Deploy to GitHub Pages
on:
push:
branches:
- main
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
jobs:
deploy:
name: Deploy to GitHub Pages
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build website
run: yarn build
# Popular action to deploy to GitHub Pages:
# Docs: https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-docusaurus
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
# Build output to publish to the `gh-pages` branch:
publish_dir: ./build
# The following lines assign commit authorship to the official
# GH-Actions bot for deploys to `gh-pages` branch:
# https://github.com/actions/checkout/issues/13#issuecomment-724415212
# The GH actions bot is used by default if you didn't specify the two fields.
# You can swap them out with your own user credentials.
user_name: github-actions[bot]
user_email: 41898282+github-actions[bot]@users.noreply.github.com
```
```yml title=".github/workflows/test-deploy.yml"
name: Test deployment
on:
pull_request:
branches:
- main
# Review gh actions docs if you want to further define triggers, paths, etc
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#on
jobs:
test-deploy:
name: Test deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Test build website
run: yarn build
```
</details>
```mdx-code-block
</TabItem>
<TabItem value="remote" label="Remote">
```
A cross-repo publish is more difficult to set up, because you need to push to another repo with permission checks. We will be using SSH to do the authentication.
1. Generate a new [SSH key](https://help.github.com/en/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent). Since this SSH key will be used in CI, make sure to not enter any passphrase.
2. By default, your public key should have been created in `~/.ssh/id_rsa.pub`; otherwise, use the name you've provided in the previous step to add your key to [GitHub deploy keys](https://developer.github.com/v3/guides/managing-deploy-keys/).
3. Copy the key to clipboard with `pbcopy < ~/.ssh/id_rsa.pub` and paste it as a [deploy key](https://developer.github.com/v3/guides/managing-deploy-keys/#deploy-keys) in the deployment repository. Copy the file content if the command line doesn't work for you. Check the box for `Allow write access` before saving your deployment key.
4. You'll need your private key as a [GitHub secret](https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets) to allow Docusaurus to run the deployment for you.
5. Copy your private key with `pbcopy < ~/.ssh/id_rsa` and paste a GitHub secret with the name `GH_PAGES_DEPLOY` on your source repository. Copy the file content if the command line doesn't work for you. Save your secret.
6. Create your [documentation workflow file](https://help.github.com/en/actions/configuring-and-managing-workflows/configuring-a-workflow#creating-a-workflow-file) in `.github/workflows/`. In this example it's `deploy.yml`.
7. You should have essentially: the source repo with the GitHub workflow set with the private SSH key as GitHub Secret and your deployment repo set with the public SSH key in GitHub Deploy Keys.
<details>
<summary>GitHub action file</summary>
:::warning
Please make sure that you replace `actions@github.com` with your GitHub email and `gh-actions` with your name.
This file assumes you are using Yarn. If you use npm, change `cache: yarn`, `yarn install --frozen-lockfile`, `yarn build` to `cache: npm`, `npm ci`, `npm run build` accordingly.
:::
```yml title=".github/workflows/deploy.yml"
name: Deploy to GitHub Pages
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
test-deploy:
if: github.event_name != 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Test build website
run: yarn build
deploy:
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
cache: yarn
- uses: webfactory/ssh-agent@v0.5.0
with:
ssh-private-key: ${{ secrets.GH_PAGES_DEPLOY }}
- name: Deploy to GitHub Pages
env:
USE_SSH: true
run: |
git config --global user.email "actions@github.com"
git config --global user.name "gh-actions"
yarn install --frozen-lockfile
yarn deploy
```
</details>
```mdx-code-block
</TabItem>
</Tabs>
```
<details>
<summary>Site not deployed properly?</summary>
After pushing to main, if you don't see your site published at the desired location (for example, it says "There isn't a GitHub Pages site here", or it's showing your repo's README.md file), check the following:
- It may take a few minutes for GitHub pages to pick up the new files, so wait for about 3 minutes and refresh before concluding it isn't working.
- On your repo's landing page, you should see a little green tick next to the last commit's title, indicating the CI has passed. If you see a cross, it means the build or deployment failed, and you should check the log for more debugging information.
- Click on the tick and make sure your see a "Deploy to GitHub Pages" workflow. Names like "pages build and deployment / deploy" are GitHub's default workflows, indicating your custom deployment workflow failed to be triggered at all. Make sure the YAML files are put under the `.github/workflows` folder, and the trigger condition is set correctly (for example, if your default branch is "master" instead of "main", you need to change the `on.push` property).
- We are using `gh-pages` as the deployment branch. Under your repo's Settings > Pages, make sure the "Source" (which is the source for the _deployment_ files, not "source" as in our terminology) is set to "gh-pages" + "/ (root)".
- If you are using a custom domain, make sure the DNS record is pointing to the GitHub pages servers' IP.
</details>
### Triggering deployment with Travis CI {#triggering-deployment-with-travis-ci}
Continuous integration (CI) services are typically used to perform routine tasks whenever new commits are checked in to source control. These tasks can be any combination of running unit tests and integration tests, automating builds, publishing packages to npm, and deploying changes to your website. All you need to do to automate the deployment of your website is to invoke the `yarn deploy` script whenever your website is updated. The following section covers how to do just that using [Travis CI](https://travis-ci.com/), a popular continuous integration service provider.
1. Go to https://github.com/settings/tokens and generate a new [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/). When creating the token, grant it the `repo` scope so that it has the permissions it needs.
2. Using your GitHub account, [add the Travis CI app](https://github.com/marketplace/travis-ci) to the repository you want to activate.
3. Open your Travis CI dashboard. The URL looks like `https://travis-ci.com/USERNAME/REPO`, and navigate to the `More options > Setting > Environment Variables` section of your repository.
4. Create a new environment variable named `GH_TOKEN` with your newly generated token as its value, then `GH_EMAIL` (your email address) and `GH_NAME` (your GitHub username).
5. Create a `.travis.yml` on the root of your repository with the following:
```yml title=".travis.yml"
language: node_js
node_js:
- 18
branches:
only:
- main
cache:
yarn: true
script:
- git config --global user.name "${GH_NAME}"
- git config --global user.email "${GH_EMAIL}"
- echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc
- yarn install
- GIT_USER="${GH_NAME}" yarn deploy
```
Now, whenever a new commit lands in `main`, Travis CI will run your suite of tests and if everything passes, your website will be deployed via the `yarn deploy` script.
### Triggering deployment with Buddy {#triggering-deployment-with-buddy}
[Buddy](https://buddy.works/) is an easy-to-use CI/CD tool that allows you to automate the deployment of your portal to different environments, including GitHub Pages.
Follow these steps to create a pipeline that automatically deploys a new version of your website whenever you push changes to the selected branch of your project:
1. Go to https://github.com/settings/tokens and generate a new [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/). When creating the token, grant it the `repo` scope so that it has the permissions it needs.
2. Sign in to your Buddy account and create a new project.
3. Choose GitHub as your git hosting provider and select the repository with the code of your website.
4. Using the left navigation panel, switch to the `Pipelines` view.
5. Create a new pipeline. Define its name, set the trigger mode to `On push`, and select the branch that triggers the pipeline execution.
6. Add a `Node.js` action.
7. Add these commands in the action's terminal:
```bash
GIT_USER=<GH_PERSONAL_ACCESS_TOKEN>
git config --global user.email "<YOUR_GH_EMAIL>"
git config --global user.name "<YOUR_GH_USERNAME>"
yarn deploy
```
After creating this simple pipeline, each new commit pushed to the branch you selected deploys your website to GitHub Pages using `yarn deploy`. Read [this guide](https://buddy.works/guides/react-docusaurus) to learn more about setting up a CI/CD pipeline for Docusaurus.
### Using Azure Pipelines {#using-azure-pipelines}
1. Sign Up at [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) if you haven't already.
2. Create an organization. Within the organization, create a project and connect your repository from GitHub.
3. Go to https://github.com/settings/tokens and generate a new [personal access token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/) with the `repo` scope.
4. In the project page (which looks like `https://dev.azure.com/ORG_NAME/REPO_NAME/_build`), create a new pipeline with the following text. Also, click on edit and add a new environment variable named `GH_TOKEN` with your newly generated token as its value, then `GH_EMAIL` (your email address) and `GH_NAME` (your GitHub username). Make sure to mark them as secret. Alternatively, you can also add a file named `azure-pipelines.yml` at your repository root.
```yml title="azure-pipelines.yml"
trigger:
- main
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
persistCredentials: true
- task: NodeTool@0
inputs:
versionSpec: '18'
displayName: Install Node.js
- script: |
git config --global user.name "${GH_NAME}"
git config --global user.email "${GH_EMAIL}"
git checkout -b main
echo "machine github.com login ${GH_NAME} password ${GH_TOKEN}" > ~/.netrc
yarn install
GIT_USER="${GH_NAME}" yarn deploy
env:
GH_NAME: $(GH_NAME)
GH_EMAIL: $(GH_EMAIL)
GH_TOKEN: $(GH_TOKEN)
displayName: Install and build
```
### Using Drone {#using-drone}
1. Create a new SSH key that will be the [deploy key](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys) for your project.
2. Name your private and public keys to be specific and so that it does not overwrite your other [SSH keys](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent).
3. Go to `https://github.com/USERNAME/REPO/settings/keys` and add a new deploy key by pasting in the public key you just generated.
4. Open your Drone.io dashboard and log in. The URL looks like `https://cloud.drone.io/USERNAME/REPO`.
5. Click on the repository, click on activate repository, and add a secret called `git_deploy_private_key` with your private key value that you just generated.
6. Create a `.drone.yml` on the root of your repository with the below text.
```yml title=".drone.yml"
kind: pipeline
type: docker
trigger:
event:
- tag
- name: Website
image: node
commands:
- mkdir -p $HOME/.ssh
- ssh-keyscan -t rsa github.com >> $HOME/.ssh/known_hosts
- echo "$GITHUB_PRIVATE_KEY" > "$HOME/.ssh/id_rsa"
- chmod 0600 $HOME/.ssh/id_rsa
- cd website
- yarn install
- yarn deploy
environment:
USE_SSH: true
GITHUB_PRIVATE_KEY:
from_secret: git_deploy_private_key
```
Now, whenever you push a new tag to GitHub, this trigger will start the drone CI job to publish your website.
## Deploying to Koyeb {#deploying-to-koyeb}
[Koyeb](https://www.koyeb.com) is a developer-friendly serverless platform to deploy apps globally. The platform lets you seamlessly run Docker containers, web apps, and APIs with git-based deployment, native autoscaling, a global edge network, and built-in service mesh and discovery. Check out the [Koyeb's Docusaurus deployment guide](https://www.koyeb.com/tutorials/deploy-docusaurus-on-koyeb) to get started.
## Deploying to Render {#deploying-to-render}
[Render](https://render.com) offers [free static site hosting](https://render.com/docs/static-sites) with fully managed SSL, custom domains, a global CDN, and continuous auto-deploy from your Git repo. Get started in just a few minutes by following [Render's guide to deploying Docusaurus](https://render.com/docs/deploy-docusaurus).
## Deploying to Qovery {#deploying-to-qovery}
[Qovery](https://www.qovery.com) is a fully-managed cloud platform that runs on your AWS, Digital Ocean, and Scaleway account where you can host static sites, backend APIs, databases, cron jobs, and all your other apps in one place.
1. Create a Qovery account. Visit the [Qovery dashboard](https://console.qovery.com) to create an account if you don't already have one.
2. Create a project.
- Click on **Create project** and give a name to your project.
- Click on **Next**.
3. Create a new environment.
- Click on **Create environment** and give a name (e.g. staging, production).
4. Add an application.
- Click on **Create an application**, give a name and select your GitHub or GitLab repository where your Docusaurus app is located.
- Define the main branch name and the root application path.
- Click on **Create**. After the application is created:
- Navigate to your application **Settings**
- Select **Port**
- Add port used by your Docusaurus application
5. Deploy All you have to do now is to navigate to your application and click on **Deploy**.
![Deploy the app](https://hub.qovery.com/img/heroku/heroku-1.png)
That's it. Watch the status and wait till the app is deployed. To open the application in your browser, click on **Action** and **Open** in your application overview.
## Deploying to Hostman {#deploying-to-hostman}
[Hostman](https://hostman.com/) allows you to host static websites for free. Hostman automates everything, you just need to connect your repository and follow easy steps:
1. Create a service.
To deploy a Docusaurus static website, click **Create** in the top-left corner of your [Dashboard](https://dashboard.hostman.com/) and choose **Front-end app or static website**.
2. Select the project to deploy.
If you are logged in to Hostman with your GitHub, GitLab, or Bitbucket account, at this point you will see the repository with your projects, including the private ones.
Choose the project you want to deploy. It must contain the directory with the project's files (e.g. `website`).
To access a different repository, click **Connect another repository**.
If you didn't use your Git account credentials to log in, you'll be able to access the necessary account now, and then select the project.
3. Configure the build settings.
Next, the **Website customization** window will appear. Choose the **Static website** option from the list of frameworks.
The **Directory with app** points at the directory that will contain the project's files after the build. You can leave it empty if during Step 2 you selected the repository with the contents of the website (or `my_website`) directory.
The standard build command for Docusaurus will be:
```bash npm2yarn
npm run build
```
You can modify the build command if needed. You can enter multiple commands separated by `&&`.
4. Deploy.
Click **Deploy** to start the build process.
Once it starts, you will enter the deployment log. If there are any issues with the code, you will get warning or error messages in the log, specifying the cause of the problem. Usually, the log contains all the debugging data you'll need.
When the deployment is complete, you will receive an email notification and also see a log entry. All done! Your project is up and ready.
## Deploying to Surge {#deploying-to-surge}
Surge is a [static web hosting platform](https://surge.sh/help/getting-started-with-surge), it is used to deploy your Docusaurus project from the command line in a minute. Deploying your project to Surge is easy and it is also free (including a custom domain and SSL).
Deploy your app in a matter of seconds using surge with the following steps:
1. First, install Surge using npm by running the following command:
```bash npm2yarn
npm install -g surge
```
2. To build the static files of your site for production in the root directory of your project, run:
```bash npm2yarn
npm run build
```
3. Then, run this command inside the root directory of your project:
```bash
surge build/
```
First-time users of Surge would be prompted to create an account from the command line (which happens only once).
Confirm that the site you want to publish is in the `build` directory, a randomly generated subdomain `*.surge.sh subdomain` is always given (which can be edited).
### Using your domain {#using-your-domain}
If you have a domain name you can deploy your site using surge to your domain using the command:
```bash
surge build/ your-domain.com
```
Your site is now deployed for free at `subdomain.surge.sh` or `your-domain.com` depending on the method you chose.
### Setting up CNAME file {#setting-up-cname-file}
Store your domain in a CNAME file for future deployments with the following command:
```bash
echo subdomain.surge.sh > CNAME
```
You can deploy any other changes in the future with the command `surge`.
## Deploying to QuantCDN {#deploying-to-quantcdn}
1. Install [Quant CLI](https://docs.quantcdn.io/docs/cli/get-started)
2. Create a QuantCDN account by [signing up](https://dashboard.quantcdn.io/register)
3. Initialize your project with `quant init` and fill in your credentials:
```bash
quant init
```
4. Deploy your site.
```bash
quant deploy
```
See [docs](https://docs.quantcdn.io/docs/cli/continuous-integration) and [blog](https://www.quantcdn.io/blog) for more examples and use cases for deploying to QuantCDN.
## Deploying to Layer0 {#deploying-to-layer0}
[Layer0](https://www.layer0.co) is an all-in-one platform to develop, deploy, preview, experiment on, monitor, and run your headless frontend. It is focused on large, dynamic websites and best-in-class performance through EdgeJS (a JavaScript-based Content Delivery Network), predictive prefetching, and performance monitoring. Layer0 offers a free tier. Get started in just a few minutes by following [Layer0's guide to deploying Docusaurus](https://docs.layer0.co/guides/docusaurus).
## Deploying to Cloudflare Pages {#deploying-to-cloudflare-pages}
[Cloudflare Pages](https://pages.cloudflare.com/) is a Jamstack platform for frontend developers to collaborate and deploy websites. Get started within a few minutes by following [this article](https://dev.to/apidev234/deploying-docusaurus-to-cloudflare-pages-565g).
## Deploying to Azure Static Web Apps {#deploying-to-azure-static-web-apps}
[Azure Static Web Apps](https://docs.microsoft.com/en-us/azure/static-web-apps/overview) is a service that automatically builds and deploys full-stack web apps to Azure directly from the code repository, simplifying the developer experience for CI/CD. Static Web Apps separates the web application's static assets from its dynamic (API) endpoints. Static assets are served from globally-distributed content servers, making it faster for clients to retrieve files using servers nearby. Dynamic APIs are scaled with serverless architectures, using an event-driven functions-based approach that is more cost-effective and scales on demand. Get started in a few minutes by following [this step-by-step guide](https://dev.to/azure/11-share-content-with-docusaurus-azure-static-web-apps-30hc).

View file

@ -0,0 +1,727 @@
---
sidebar_label: Client API
---
# Docusaurus Client API
Docusaurus provides some APIs on the clients that can be helpful to you when building your site.
## Components {#components}
### `<ErrorBoundary />` {#errorboundary}
This component creates a [React error boundary](https://reactjs.org/docs/error-boundaries.html).
Use it to wrap components that might throw, and display a fallback when that happens instead of crashing the whole app.
```jsx
import React from 'react';
import ErrorBoundary from '@docusaurus/ErrorBoundary';
const SafeComponent = () => (
<ErrorBoundary
fallback={({error, tryAgain}) => (
<div>
<p>This component crashed because of error: {error.message}.</p>
<button onClick={tryAgain}>Try Again!</button>
</div>
)}>
<SomeDangerousComponentThatMayThrow />
</ErrorBoundary>
);
```
```mdx-code-block
import ErrorBoundaryTestButton from "@site/src/components/ErrorBoundaryTestButton"
```
:::tip
To see it in action, click here: <ErrorBoundaryTestButton/>
:::
:::info
Docusaurus uses this component to catch errors within the theme's layout, and also within the entire app.
:::
:::note
This component doesn't catch build-time errors and only protects against client-side render errors that can happen when using stateful React components.
:::
#### Props {#errorboundary-props}
- `fallback`: an optional render callback returning a JSX element. It will receive an object with 2 attributes: `error`, the error that was caught, and `tryAgain`, a function (`() => void`) callback to reset the error in the component and try rendering it again. If not present, `@theme/Error` will be rendered instead. `@theme/Error` is used for the error boundaries wrapping the site, above the layout.
:::caution
The `fallback` prop is a callback, and **not a React functional component**. You can't use React hooks inside this callback.
:::
### `<Head/>` {#head}
This reusable React component will manage all of your changes to the document head. It takes plain HTML tags and outputs plain HTML tags and is beginner-friendly. It is a wrapper around [React Helmet](https://github.com/nfl/react-helmet).
Usage Example:
```jsx
import React from 'react';
// highlight-next-line
import Head from '@docusaurus/Head';
const MySEO = () => (
// highlight-start
<Head>
<meta property="og:description" content="My custom description" />
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://mysite.com/example" />
</Head>
// highlight-end
);
```
Nested or latter components will override duplicate usages:
```jsx
<Parent>
{/* highlight-start */}
<Head>
<title>My Title</title>
<meta name="description" content="Helmet application" />
</Head>
{/* highlight-end */}
<Child>
{/* highlight-start */}
<Head>
<title>Nested Title</title>
<meta name="description" content="Nested component" />
</Head>
{/* highlight-end */}
</Child>
</Parent>
```
Outputs:
```html
<head>
<title>Nested Title</title>
<meta name="description" content="Nested component" />
</head>
```
### `<Link/>` {#link}
This component enables linking to internal pages as well as a powerful performance feature called preloading. Preloading is used to prefetch resources so that the resources are fetched by the time the user navigates with this component. We use an `IntersectionObserver` to fetch a low-priority request when the `<Link>` is in the viewport and then use an `onMouseOver` event to trigger a high-priority request when it is likely that a user will navigate to the requested resource.
The component is a wrapper around react-routers `<Link>` component that adds useful enhancements specific to Docusaurus. All props are passed through to react-routers `<Link>` component.
External links also work, and automatically have these props: `target="_blank" rel="noopener noreferrer"`.
```jsx
import React from 'react';
// highlight-next-line
import Link from '@docusaurus/Link';
const Page = () => (
<div>
<p>
{/* highlight-next-line */}
Check out my <Link to="/blog">blog</Link>!
</p>
<p>
{/* highlight-next-line */}
Follow me on <Link to="https://twitter.com/docusaurus">Twitter</Link>!
</p>
</div>
);
```
#### `to`: string {#to-string}
The target location to navigate to. Example: `/docs/introduction`.
```jsx
<Link to="/courses" />
```
:::tip
Prefer this component to vanilla `<a>` tags because Docusaurus does a lot of optimizations (e.g. broken path detection, prefetching, applying base URL...) if you use `<Link>`.
:::
### `<Redirect/>` {#redirect}
Rendering a `<Redirect>` will navigate to a new location. The new location will override the current location in the history stack like server-side redirects (HTTP 3xx) do. You can refer to [React Router's Redirect documentation](https://reacttraining.com/react-router/web/api/Redirect) for more info on available props.
Example usage:
```jsx
import React from 'react';
// highlight-next-line
import {Redirect} from '@docusaurus/router';
const Home = () => {
// highlight-next-line
return <Redirect to="/docs/test" />;
};
```
:::note
`@docusaurus/router` implements [React Router](https://reacttraining.com/react-router/web/guides/quick-start) and supports its features.
:::
### `<BrowserOnly/>` {#browseronly}
The `<BrowserOnly>` component permits to render React components only in the browser after the React app has hydrated.
:::tip
Use it for integrating with code that can't run in Node.js, because the `window` or `document` objects are being accessed.
:::
#### Props {#browseronly-props}
- `children`: render function prop returning browser-only JSX. Will not be executed in Node.js
- `fallback` (optional): JSX to render on the server (Node.js) and until React hydration completes.
#### Example with code {#browseronly-example-code}
```jsx
// highlight-start
import BrowserOnly from '@docusaurus/BrowserOnly';
// highlight-end
const MyComponent = () => {
return (
// highlight-start
<BrowserOnly>
{() => <span>page url = {window.location.href}</span>}
</BrowserOnly>
// highlight-end
);
};
```
#### Example with a library {#browseronly-example-library}
```jsx
// highlight-start
import BrowserOnly from '@docusaurus/BrowserOnly';
// highlight-end
const MyComponent = (props) => {
return (
// highlight-start
<BrowserOnly fallback={<div>Loading...</div>}>
{() => {
const LibComponent = require('some-lib').LibComponent;
return <LibComponent {...props} />;
}}
</BrowserOnly>
// highlight-end
);
};
```
### `<Interpolate/>` {#interpolate}
A simple interpolation component for text containing dynamic placeholders.
The placeholders will be replaced with the provided dynamic values and JSX elements of your choice (strings, links, styled elements...).
#### Props {#interpolate-props}
- `children`: text containing interpolation placeholders like `{placeholderName}`
- `values`: object containing interpolation placeholder values
```jsx
import React from 'react';
import Link from '@docusaurus/Link';
import Interpolate from '@docusaurus/Interpolate';
export default function VisitMyWebsiteMessage() {
return (
// highlight-start
<Interpolate
values={{
firstName: 'Sébastien',
website: (
<Link to="https://docusaurus.io" className="my-website-class">
website
</Link>
),
}}>
{'Hello, {firstName}! How are you? Take a look at my {website}'}
</Interpolate>
// highlight-end
);
}
```
### `<Translate/>` {#translate}
When [localizing your site](./i18n/i18n-introduction.mdx), the `<Translate/>` component will allow providing **translation support to React components**, such as your homepage. The `<Translate>` component supports [interpolation](#interpolate).
The translation strings will statically extracted from your code with the [`docusaurus write-translations`](./cli.mdx#docusaurus-write-translations-sitedir) CLI and a `code.json` translation file will be created in `website/i18n/[locale]`.
:::note
The `<Translate/>` props **must be hardcoded strings**.
Apart from the `values` prop used for interpolation, it is **not possible to use variables**, or the static extraction wouldn't work.
:::
#### Props {#translate-props}
- `children`: untranslated string in the default site locale (can contain [interpolation placeholders](#interpolate))
- `id`: optional value to be used as the key in JSON translation files
- `description`: optional text to help the translator
- `values`: optional object containing interpolation placeholder values
#### Example {#example}
```jsx title="src/pages/index.js"
import React from 'react';
import Layout from '@theme/Layout';
// highlight-start
import Translate from '@docusaurus/Translate';
// highlight-end
export default function Home() {
return (
<Layout>
<h1>
{/* highlight-start */}
<Translate
id="homepage.title"
description="The homepage welcome message">
Welcome to my website
</Translate>
{/* highlight-end */}
</h1>
<main>
{/* highlight-start */}
<Translate values={{firstName: 'Sébastien'}}>
{'Welcome, {firstName}! How are you?'}
</Translate>
{/* highlight-end */}
</main>
</Layout>
);
}
```
:::note
You can even omit the children prop and specify a translation string in your `code.json` file manually after running the `docusaurus write-translations` CLI command.
```jsx
<Translate id="homepage.title" />
```
:::
## Hooks {#hooks}
### `useDocusaurusContext` {#useDocusaurusContext}
React hook to access Docusaurus Context. The context contains the `siteConfig` object from [docusaurus.config.js](api/docusaurus.config.js.mdx) and some additional site metadata.
```ts
type PluginVersionInformation =
| {readonly type: 'package'; readonly version?: string}
| {readonly type: 'project'}
| {readonly type: 'local'}
| {readonly type: 'synthetic'};
type SiteMetadata = {
readonly docusaurusVersion: string;
readonly siteVersion?: string;
readonly pluginVersions: Record<string, PluginVersionInformation>;
};
type I18nLocaleConfig = {
label: string;
direction: string;
};
type I18n = {
defaultLocale: string;
locales: [string, ...string[]];
currentLocale: string;
localeConfigs: Record<string, I18nLocaleConfig>;
};
type DocusaurusContext = {
siteConfig: DocusaurusConfig;
siteMetadata: SiteMetadata;
globalData: Record<string, unknown>;
i18n: I18n;
codeTranslations: Record<string, string>;
};
```
Usage example:
```jsx
import React from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
const MyComponent = () => {
// highlight-next-line
const {siteConfig, siteMetadata} = useDocusaurusContext();
return (
<div>
{/* highlight-start */}
<h1>{siteConfig.title}</h1>
<div>{siteMetadata.siteVersion}</div>
<div>{siteMetadata.docusaurusVersion}</div>
{/* highlight-end */}
</div>
);
};
```
:::note
The `siteConfig` object only contains **serializable values** (values that are preserved after `JSON.stringify()`). Functions, regexes, etc. would be lost on the client side.
:::
### `useIsBrowser` {#useIsBrowser}
Returns `true` when the React app has successfully hydrated in the browser.
:::caution
Use this hook instead of `typeof windows !== 'undefined'` in React rendering logic.
The first client-side render output (in the browser) **must be exactly the same** as the server-side render output (Node.js). Not following this rule can lead to unexpected hydration behaviors, as described in [The Perils of Rehydration](https://www.joshwcomeau.com/react/the-perils-of-rehydration/).
:::
Usage example:
```jsx
import React from 'react';
import useIsBrowser from '@docusaurus/useIsBrowser';
const MyComponent = () => {
// highlight-start
const isBrowser = useIsBrowser();
// highlight-end
return <div>{isBrowser ? 'Client' : 'Server'}</div>;
};
```
### `useBaseUrl` {#useBaseUrl}
React hook to prepend your site `baseUrl` to a string.
:::caution
**Don't use it for regular links!**
The `/baseUrl/` prefix is automatically added to all **absolute paths** by default:
- Markdown: `[link](/my/path)` will link to `/baseUrl/my/path`
- React: `<Link to="/my/path/">link</Link>` will link to `/baseUrl/my/path`
:::
#### Options {#options}
```ts
type BaseUrlOptions = {
forcePrependBaseUrl: boolean;
absolute: boolean;
};
```
#### Example usage: {#example-usage}
```jsx
import React from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';
const SomeImage = () => {
// highlight-start
const imgSrc = useBaseUrl('/img/myImage.png');
// highlight-end
return <img src={imgSrc} />;
};
```
:::tip
In most cases, you don't need `useBaseUrl`.
Prefer a `require()` call for [assets](./guides/markdown-features/markdown-features-assets.mdx):
```jsx
<img src={require('@site/static/img/myImage.png').default} />
```
:::
### `useBaseUrlUtils` {#useBaseUrlUtils}
Sometimes `useBaseUrl` is not good enough. This hook return additional utils related to your site's base URL.
- `withBaseUrl`: useful if you need to add base URLs to multiple URLs at once.
```jsx
import React from 'react';
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
const Component = () => {
const urls = ['/a', '/b'];
// highlight-start
const {withBaseUrl} = useBaseUrlUtils();
const urlsWithBaseUrl = urls.map(withBaseUrl);
// highlight-end
return <div>{/* ... */}</div>;
};
```
### `useGlobalData` {#useGlobalData}
React hook to access Docusaurus global data created by all the plugins.
Global data is namespaced by plugin name then by plugin ID.
:::info
Plugin ID is only useful when a plugin is used multiple times on the same site. Each plugin instance is able to create its own global data.
:::
```ts
type GlobalData = Record<
PluginName,
Record<
PluginId, // "default" by default
any // plugin-specific data
>
>;
```
Usage example:
```jsx
import React from 'react';
// highlight-next-line
import useGlobalData from '@docusaurus/useGlobalData';
const MyComponent = () => {
// highlight-start
const globalData = useGlobalData();
const myPluginData = globalData['my-plugin']['default'];
return <div>{myPluginData.someAttribute}</div>;
// highlight-end
};
```
:::tip
Inspect your site's global data at `./docusaurus/globalData.json`
:::
### `usePluginData` {#usePluginData}
Access global data created by a specific plugin instance.
This is the most convenient hook to access plugin global data and should be used most of the time.
`pluginId` is optional if you don't use multi-instance plugins.
```ts
function usePluginData(
pluginName: string,
pluginId?: string,
options?: {failfast?: boolean},
);
```
Usage example:
```jsx
import React from 'react';
// highlight-next-line
import {usePluginData} from '@docusaurus/useGlobalData';
const MyComponent = () => {
// highlight-start
const myPluginData = usePluginData('my-plugin');
return <div>{myPluginData.someAttribute}</div>;
// highlight-end
};
```
### `useAllPluginInstancesData` {#useAllPluginInstancesData}
Access global data created by a specific plugin. Given a plugin name, it returns the data of all the plugins instances of that name, by plugin id.
```ts
function useAllPluginInstancesData(
pluginName: string,
options?: {failfast?: boolean},
);
```
Usage example:
```jsx
import React from 'react';
// highlight-next-line
import {useAllPluginInstancesData} from '@docusaurus/useGlobalData';
const MyComponent = () => {
// highlight-start
const allPluginInstancesData = useAllPluginInstancesData('my-plugin');
const myPluginData = allPluginInstancesData['default'];
return <div>{myPluginData.someAttribute}</div>;
// highlight-end
};
```
## Functions {#functions}
### `interpolate` {#interpolate-1}
The imperative counterpart of the [`<Interpolate>`](#interpolate) component.
#### Signature {#signature}
```ts
// Simple string interpolation
function interpolate(text: string, values: Record<string, string>): string;
// JSX interpolation
function interpolate(
text: string,
values: Record<string, ReactNode>,
): ReactNode;
```
#### Example {#example-1}
```js
// highlight-next-line
import {interpolate} from '@docusaurus/Interpolate';
const message = interpolate('Welcome {firstName}', {firstName: 'Sébastien'});
```
### `translate` {#translate-imperative}
The imperative counterpart of the [`<Translate>`](#translate) component. Also supporting [placeholders interpolation](#interpolate).
:::tip
Use the imperative API for the **rare cases** where a **component cannot be used**, such as:
- the page `title` metadata
- the `placeholder` props of form inputs
- the `aria-label` props for accessibility
:::
#### Signature {#signature-1}
```ts
function translate(
translation: {message: string; id?: string; description?: string},
values: Record<string, string>,
): string;
```
#### Example {#example-2}
```jsx title="src/pages/index.js"
import React from 'react';
import Layout from '@theme/Layout';
// highlight-next-line
import {translate} from '@docusaurus/Translate';
export default function Home() {
return (
<Layout
// highlight-next-line
title={translate({message: 'My page meta title'})}>
<img
src={'https://docusaurus.io/logo.png'}
aria-label={
// highlight-start
translate(
{
message: 'The logo of site {siteName}',
// Optional
id: 'homepage.logo.ariaLabel',
description: 'The home page logo aria label',
},
{siteName: 'Docusaurus'},
)
// highlight-end
}
/>
</Layout>
);
}
```
## Modules {#modules}
### `ExecutionEnvironment` {#executionenvironment}
A module that exposes a few boolean variables to check the current rendering environment.
:::caution
For React rendering logic, use [`useIsBrowser()`](#useIsBrowser) or [`<BrowserOnly>`](#browseronly) instead.
:::
Example:
```js
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
if (ExecutionEnvironment.canUseDOM) {
require('lib-that-only-works-client-side');
}
```
| Field | Description |
| --- | --- |
| `ExecutionEnvironment.canUseDOM` | `true` if on client/browser, `false` on Node.js/prerendering. |
| `ExecutionEnvironment.canUseEventListeners` | `true` if on client and has `window.addEventListener`. |
| `ExecutionEnvironment.canUseIntersectionObserver` | `true` if on client and has `IntersectionObserver`. |
| `ExecutionEnvironment.canUseViewport` | `true` if on client and has `window.screen`. |
### `constants` {#constants}
A module exposing useful constants to client-side theme code.
```js
import {DEFAULT_PLUGIN_ID} from '@docusaurus/constants';
```
| Named export | Value |
| ------------------- | --------- |
| `DEFAULT_PLUGIN_ID` | `default` |

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
.
```
:::caution
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`, 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,162 @@
---
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](#dog-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}
Optionally, you can add tags to your doc pages, which introduces another dimension of categorization in addition to the [docs sidebar](./sidebar/index.mdx). Tags are passed in the front matter as a list of labels:
```md "your-doc-page.md"
---
id: doc-with-tags
title: A doc with tags
tags:
- Demo
- Getting started
---
```
:::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. 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,116 @@
---
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"
module.exports = {
// ...
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"
module.exports = {
// ...
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/.
```
:::caution
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 2. 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.
:::caution
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.
:::caution
`@docusaurus/preset-classic` already includes a docs plugin instance for you!
:::
When using the preset:
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-start
// id: 'product', // omitted => default instance
// highlight-end
path: 'product',
routeBasePath: 'product',
sidebarPath: require.resolve('./sidebarsProduct.js'),
// ... other options
},
},
],
],
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
id: 'community',
// highlight-end
path: 'community',
routeBasePath: 'community',
sidebarPath: require.resolve('./sidebarsCommunity.js'),
// ... other options
},
],
],
};
```
When not using the preset:
```js title="docusaurus.config.js"
module.exports = {
plugins: [
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
// id: 'product', // omitted => default instance
// highlight-end
path: 'product',
routeBasePath: 'product',
sidebarPath: require.resolve('./sidebarsProduct.js'),
// ... other options
},
],
[
'@docusaurus/plugin-content-docs',
{
// highlight-start
id: 'community',
// highlight-end
path: 'community',
routeBasePath: 'community',
sidebarPath: require.resolve('./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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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/hard
},
// 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"
module.exports = {
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/hard
'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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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](items.mdx#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 Hard
│ ├── 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.
:::caution
**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"
module.exports = {
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
module.exports = {
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,209 @@
---
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"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
sidebarPath: require.resolve('./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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
themeConfig: {
// highlight-start
docs: {
sidebar: {
autoCollapseCategories: true,
},
},
// highlight-end
},
};
```
## Passing custom props {#passing-custom-props}
To pass in custom props to a swizzled sidebar item, add the optional `customProps` object to any of the items:
```js
{
type: 'doc',
id: 'doc1',
customProps: {
/* props */
},
};
```
## 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"
module.exports = {
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.js')
.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"
module.exports = {
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](#sidebar-item-autogenerated) 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](#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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-next-line
sidebarCollapsed: false,
},
},
],
],
};
```
:::caution
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"
module.exports = {
sidebar: [
// highlight-start
{
type: 'doc',
id: 'myDoc',
},
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="Shorthand">
```
```js title="sidebars.js"
module.exports = {
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"
module.exports = {
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"
module.exports = {
sidebar: [
// highlight-start
{
type: 'category',
label: 'Getting started',
items: ['doc1', 'doc2'],
},
// highlight-end
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="Shorthand">
```
```js title="sidebars.js"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
sidebar: [
{
'Getting started': ['doc1'],
Docusaurus: [
{
'Basic guides': ['doc2', 'doc3'],
'Advanced guides': ['doc4', 'doc5'],
},
],
},
],
};
```
```mdx-code-block
</TabItem>
<TabItem value="After">
```
```js title="sidebars.js"
module.exports = {
sidebar: {
'Getting started': ['doc1'],
Docusaurus: {
'Basic guides': ['doc2', 'doc3'],
'Advanced guides': ['doc4', 'doc5'],
},
},
};
```
```mdx-code-block
</TabItem>
</Tabs>
```

View file

@ -0,0 +1,144 @@
---
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
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,298 @@
---
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';
```
:::caution
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](docs-introduction.mdx#sidebar) 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>
```
### 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"
module.exports = {
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,256 @@
---
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`](#).
:::
:::caution
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`](#).
:::
:::caution
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
Some **content** with _Markdown_ `syntax`.
:::
```
```mdx-code-block
<BrowserWindow>
:::note Your Title
Some **content** with _Markdown_ `syntax`.
:::
</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`, `caution`. 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...">
<p>
Use plugins to introduce shorter syntax for the most commonly used JSX
elements in your project.
</p>
</Admonition>
```
```mdx-code-block
<BrowserWindow>
<Admonition type="tip" icon="💡" title="Did you know...">
<p>
Use plugins to introduce shorter syntax for the most commonly used JSX
elements in your project.
</p>
</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 MyIcon from '@site/static/img/info.svg';
export default function AdmonitionWrapper(props) {
if (props.type !== 'info') {
return <Admonition {...props} />;
}
return <Admonition icon={<MyIcon />} {...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"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
admonitions: {
tag: ':::',
keywords: ['note', 'tip', 'info', 'caution', 'danger'],
},
},
},
],
],
};
```
The plugin accepts two options:
- `tag`: The tag that encloses the admonition. Defaults to `:::`.
- `keywords`: An array of keywords that can be used as the type for the admonition. Note that if you override this, the default values will not be applied.
The `keyword` will be passed as the `type` prop of the `Admonition` component. If you register more types than the default, you are also responsible for providing their implementation—including the container's style, icon, default title text, etc. You would usually need to [eject](../../swizzling.mdx#ejecting) the `@theme/Admonition` component, so you could re-use the same infrastructure as the other types.

View file

@ -0,0 +1,234 @@
---
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 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,848 @@
---
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/src/themes/palenight.js). 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"
module.exports = {
themeConfig: {
prism: {
// highlight-next-line
theme: require('prism-react-renderer/themes/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.mjs) and [dark](https://github.com/facebook/docusaurus/blob/main/website/src/utils/prismDark.mjs) 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/src/vendor/prism/includeLangs.js).
:::caution
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"
module.exports = {
// ...
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.
## 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
module.exports = {
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}
module.exports = {
// ...
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}
:::caution 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>
:::caution 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.
:::
:::caution
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"
module.exports = {
// ...
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,85 @@
---
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"
module.exports = {
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"
module.exports = {
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"
module.exports = {
themeConfig: {
mermaid: {
options: {
maxTextSize: 50,
},
},
},
};
```
See the [Mermaid configuration documentation](https://mermaid-js.github.io/mermaid/#/./Setup?id=configuration) for the available config options.

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,141 @@
---
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';
Documentation is one of your product's interfaces with your users. A well-written and well-organized set of docs helps your users understand your product quickly. Our aligned goal here is to help your users find and understand the information they need, as quickly as possible.
Docusaurus 2 uses modern tooling to help you compose your interactive documentation with ease. You may embed React components, or build live coding blocks where your users may play with the code on the spot. Start sharing your eureka moments with the code your audience cannot walk away from. It is perhaps the most effective way of attracting potential users.
:::important
This section assumes you are using the official Docusaurus content plugins.
:::
## Standard features {#standard-features}
Markdown is a syntax that enables you to write formatted content in a readable syntax.
We use [MDX](https://mdxjs.com/) as the parsing engine, which can do much more than just parsing [standard Markdown syntax](https://daringfireball.net/projects/markdown/syntax), like rendering React components inside your documents as well.
```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
---
```
## 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:
```md
### Details element example
<details>
<summary>Toggle me!</summary>
<div>
<div>This is the detailed content</div>
<br/>
<details>
<summary>
Nested toggle! Some surprise inside...
</summary>
<div>😲😲😲😲😲</div>
</details>
</div>
</details>
```
```mdx-code-block
<BrowserWindow>
<h3>Details element example</h3>
<details>
<summary>Toggle me!</summary>
<div>
<div>This is the detailed content</div>
<br/>
<details>
<summary>
Nested toggle! Some surprise inside...
</summary>
<div>😲😲😲😲😲</div>
</details>
</div>
</details>
</BrowserWindow>
```
:::note
In practice, those are not really HTML elements, but React JSX elements, which we'll cover next!
:::

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,210 @@
---
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>
## Configuration {#configuration}
To enable KaTeX, you need to install `remark-math` and `rehype-katex` plugins.
```bash npm2yarn
npm install --save remark-math@3 rehype-katex@5 hast-util-is-element@1.1.0
```
:::caution
Use the exact same versions. The latest versions are incompatible with Docusaurus 2.
:::
Import the plugins in `docusaurus.config.js`:
```js
const math = require('remark-math');
const katex = require('rehype-katex');
```
Add them to your content plugin or preset options (usually `@docusaurus/preset-classic` docs options):
```js
remarkPlugins: [math],
rehypePlugins: [katex],
```
Include the KaTeX CSS in your config under `stylesheets`:
```js
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',
},
],
```
Overall the changes look like:
```js title="docusaurus.config.js"
// highlight-start
const math = require('remark-math');
const katex = require('rehype-katex');
// highlight-end
module.exports = {
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [math],
rehypePlugins: [katex],
// 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
};
```
## 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"
module.exports = {
stylesheets: [
{
href: '/katex/katex.min.css',
type: 'text/css',
},
],
};
```
## Upgrading rehype-katex beyond recommended version {#upgrading-rehype-katex-beyond-recommended-version}
:::tip
Only use the latest version if you actually need the bleeding-edge features of $\KaTeX$. Most users should find the older versions work just as well.
:::
The latest versions of `rehype-katex` (starting from `v6.0.0`) has moved to ES Modules, a new module system of JavaScript, which Docusaurus doesn't officially support yet. However, it is possible to import `rehype-katex` dynamically, using an async config creator. Docusaurus will call this creator function and wait for it to return the config object.
```js title="docusaurus.config.js"
async function createConfig() {
// ES Modules are imported with `import()` instead of `require()`, and are imported asynchronously
// highlight-next-line
const katex = (await import('rehype-katex')).default;
return {
// ...
};
}
```
In this case, the overall configuration changes will look like:
```js title="docusaurus.config.js"
// highlight-next-line
const math = require('remark-math');
async function createConfig() {
// highlight-next-line
const katex = (await import('rehype-katex')).default;
return {
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
path: 'docs',
// highlight-start
remarkPlugins: [math],
rehypePlugins: [katex],
// highlight-end
},
},
],
],
// highlight-start
stylesheets: [
{
href: 'https://cdn.jsdelivr.net/npm/katex@0.15.2/dist/katex.min.css',
type: 'text/css',
integrity:
'sha384-MlJdn/WNKDGXveldHDdyRP1R4CTHr3FeuDNfhsLPYrq2t0UBkUdK2jyTnXPEK1NQ',
crossorigin: 'anonymous',
},
],
// highlight-end
};
}
module.exports = createConfig;
```

View file

@ -0,0 +1,215 @@
---
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://mdx-git-renovate-babel-monorepo-mdx.vercel.app/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@3 rehype-katex@4
```
:::note
There's recently a trend in the Remark/Rehype ecosystem to migrate to ES Modules, a new JavaScript module system, which Docusaurus doesn't support yet. Please make sure your installed plugin version is CommonJS-compatible before we officially support ESM. Alternatively, you can read about using dynamic `import()` as a workaround in the tutorial of installing [`rehype-katex`](./markdown-features-math-equations.mdx#upgrading-rehype-katex-beyond-recommended-version).
:::
<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>
Next, add them to the plugin options through plugin or preset config in `docusaurus.config.js`:
```js title="docusaurus.config.js"
// highlight-start
const math = require('remark-math');
const katex = require('rehype-katex');
// highlight-end
module.exports = {
title: 'Docusaurus',
tagline: 'Build optimized websites quickly, focus on your content',
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-start
remarkPlugins: [math],
rehypePlugins: [katex],
// highlight-end
},
},
],
],
};
```
## 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"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
remarkPlugins: [math],
rehypePlugins: [
// highlight-next-line
[katex, {strict: false}],
],
},
},
],
],
};
```
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#creating-plugins) or [Rehype](https://github.com/remarkjs/remark/blob/main/doc/plugins.md#creating-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"
const visit = require('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;
};
module.exports = 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
const sectionPrefix = require('./src/remark/section-prefix');
module.exports = {
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"
module.exports = {
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,579 @@
---
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';
```
## Using JSX in Markdown {#using-jsx-in-markdown}
Docusaurus has built-in support for [MDX v1](https://mdxjs.com/), which allows you to write JSX within your Markdown files and render them as React components.
:::note
While Docusaurus parses both `.md` and `.mdx` files using MDX, some of the syntaxes are treated slightly differently by third-party tools. For the most accurate parsing and better editor support, we recommend using the `.mdx` extension for files containing MDX syntax.
:::
Check out the [MDX docs](https://mdxjs.com/) to see what other fancy stuff you can do with MDX.
### 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>
```
:::caution 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>
```
This behavior is different from Docusaurus 1. See also [Migrating from v1 to v2](../../migration/migration-manual.mdx#convert-style-attributes-to-style-objects-in-mdx).
In addition, MDX is not [100% compatible with CommonMark](https://github.com/facebook/docusaurus/issues/3018). Use the **[MDX playground](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/playground)** to ensure that your syntax is valid MDX.
:::
### 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 v2+ onward (Docusaurus v3+), lower-case tag names are always rendered as native html elements, and will not use any component mapping you provide.
:::
:::caution
This feature is powered by [a wrapper provider](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/advanced/components#mdxprovider). 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 v2 is using MDX v1, which has a lot of known cases where the content fails to be correctly parsed as Markdown. Use the **[MDX playground](https://mdx-git-renovate-babel-monorepo-mdx.vercel.app/playground)** to ensure that your syntax is valid MDX.
<details>
<summary>Samples of parsing failures</summary>
**A paragraph starting with a JSX tag will be seen entirely as a JSX string:**
```mdx-code-block
<Tabs groupId="jsx-and-md">
<TabItem value="Problem">
<div className={styles.wrappingBlock}>
```
```jsx
<span style={{color: 'red'}}>Highlighted text</span> but afterwards _Markdown_ **doesn't work**
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<span style={{color: 'red'}}>Highlighted text</span> but afterwards _Markdown_ **doesn't work**
</BrowserWindow>
</div>
</TabItem>
<TabItem value="Workaround">
Use JSX for the rest of the line, or prefix the line with some plain text:
<div className={styles.wrappingBlock}>
```
```jsx
<span style={{color: 'red'}}>Use JSX for the paragraph</span> to stop <i>worrying about</i> <b>Markdown</b>
&#8203;<span style={{color: 'red'}}>← This is a zero-width space</span> and afterwards <i>Markdown</i> <b>works</b>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<span style={{color: 'red'}}>Use JSX for the paragraph</span> to stop <i>worrying about</i> <b>Markdown</b>
&#8203;<span style={{color: 'red'}}>← This is a zero-width space</span> and afterwards <i>Markdown</i> <b>works</b>
</BrowserWindow>
</div>
</TabItem>
</Tabs>
**Markdown within a JSX tag never works:**
<Tabs groupId="jsx-and-md">
<TabItem value="Problem">
<div className={styles.wrappingBlock}>
```
```jsx
<span style={{color: 'red'}}>**Bold doesn't work**</span>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<span style={{color: 'red'}}>**Bold doesn't work**</span>
</BrowserWindow>
</div>
</TabItem>
<TabItem value="Workaround">
Use JSX within JSX tag, or move the Markdown to the outer layer:
<div className={styles.wrappingBlock}>
```
```jsx
<span style={{color: 'red'}}><b>Bold now works</b></span>
**<span style={{color: 'red'}}>Bold now works</span>**
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<span style={{color: 'red'}}><b>Bold now works</b></span>
**<span style={{color: 'red'}}>Bold now works</span>**
</BrowserWindow>
</div>
</TabItem>
</Tabs>
**Text immediately below a JSX tag will be seen as JSX text:**
<Tabs groupId="jsx-and-md">
<TabItem value="Problem">
<div className={styles.wrappingBlock}>
```
<!-- prettier-ignore -->
```jsx
<div style={{color: 'red'}}>
**Bold still doesn't work**
</div>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<div style={{color: 'red'}}>
**Bold still doesn't work**
</div>
</BrowserWindow>
</div>
</TabItem>
<TabItem value="Workaround">
Add an empty new line:
<div className={styles.wrappingBlock}>
```
<!-- prettier-ignore -->
```jsx
<div style={{color: 'red'}}>
**Bold now works**
</div>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<div style={{color: 'red'}}>
**Bold now works**
</div>
</BrowserWindow>
</div>
</TabItem>
</Tabs>
**Markdown text indented by four spaces will be seen as a code block:**
<Tabs groupId="jsx-and-md">
<TabItem value="Problem">
<div className={styles.wrappingBlock}>
```
<!-- prettier-ignore -->
```jsx
<div style={{color: 'red'}}>
You may think I'm just some text...
</div>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<div style={{color: 'red'}}>
You may think I'm just some text...
</div>
</BrowserWindow>
</div>
</TabItem>
<TabItem value="Workaround">
Don't indent:
<div className={styles.wrappingBlock}>
```
<!-- prettier-ignore -->
```jsx
<div style={{color: 'red'}}>
Now I'm actually just text
</div>
```
```mdx-code-block
</div>
<div className={styles.wrappingBlock}>
<BrowserWindow>
<div style={{color: 'red'}}>
Now I'm actually just text
</div>
</BrowserWindow>
</div>
</TabItem>
</Tabs>
```
</details>
## 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-start -->
```jsx title="myMarkdownFile.mdx"
import CodeBlock from '@theme/CodeBlock';
import MyComponentSource from '!!raw-loader!./myComponent';
<CodeBlock language="jsx">{MyComponentSource}</CodeBlock>
```
<!-- prettier-ignore-end -->
```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-start -->
```jsx title="someOtherDoc.mdx"
import PartialExample from './_markdown-partial-example.mdx';
<PartialExample name="Sebastien" />
```
<!-- prettier-ignore-end -->
```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.
:::caution
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,380 @@
---
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-start -->
```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>
```
<!-- prettier-ignore-end -->
```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-start -->
```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>
```
<!-- prettier-ignore-end -->
```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 deep linking: the ability to share or bookmark a link to a specific tab, that will be pre-selected when the page loads.
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**:
```md
### Hello World {#my-explicit-id}
```
:::tip
Use the **[`write-heading-ids`](../../cli.mdx#docusaurus-write-heading-ids-sitedir)** CLI command to add explicit IDs to all your Markdown documents.
:::
:::caution 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"
module.exports = {
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).
---
:::caution
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.

View file

@ -0,0 +1,516 @@
---
id: crowdin
slug: /i18n/crowdin
toc_max_heading_level: 4
---
# i18n - Using Crowdin
The i18n system of Docusaurus is **decoupled from any translation software**.
You can integrate Docusaurus with the **tools and SaaS of your choice**, as long as you put the **translation files at the correct location**.
We document the usage of [Crowdin](https://crowdin.com/), as **one** possible **integration example**.
:::caution
This is **not an endorsement of Crowdin** as the unique choice to translate a Docusaurus site, but it is successfully used by Facebook to translate documentation projects such as [Jest](https://jestjs.io/), [Docusaurus](https://docusaurus.io/), and [ReasonML](https://reasonml.github.io/).
Refer to the **[Crowdin documentation](https://support.crowdin.com/)** and **[Crowdin support](mailto:support@crowdin.com)** for help.
:::
:::tip
Use this **[community-driven GitHub discussion](https://github.com/facebook/docusaurus/discussions/4052)** to discuss anything related to Docusaurus + Crowdin.
:::
## Crowdin overview {#crowdin-overview}
Crowdin is a translation SaaS, offering a [free plan for open-source projects](https://crowdin.com/page/open-source-project-setup-request).
We recommend the following translation workflow:
- **Upload sources** to Crowdin (untranslated files)
- Use Crowdin to **translate the content**
- **Download translations** from Crowdin (localized translation files)
Crowdin provides a [CLI](https://support.crowdin.com/cli-tool/) to **upload sources** and **download translations**, allowing you to automate the translation process.
The [`crowdin.yml` configuration file](https://support.crowdin.com/configuration-file/) is convenient for Docusaurus, and permits to **download the localized translation files at the expected location** (in `i18n/[locale]/..`).
Read the **[official documentation](https://support.crowdin.com/)** to know more about advanced features and different translation workflows.
## Crowdin tutorial {#crowdin-tutorial}
This is a walk-through of using Crowdin to translate a newly initialized English Docusaurus website into French, and assume you already followed the [i18n tutorial](./i18n-tutorial.mdx).
The end result can be seen at [docusaurus-crowdin-example.netlify.app](https://docusaurus-crowdin-example.netlify.app/) ([repository](https://github.com/slorber/docusaurus-crowdin-example)).
### Prepare the Docusaurus site {#prepare-the-docusaurus-site}
Initialize a new Docusaurus site:
```bash
npx create-docusaurus@latest website classic
```
Add the site configuration for the French language:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};
```
Translate the homepage:
```jsx title="src/pages/index.js"
import React from 'react';
import Translate from '@docusaurus/Translate';
import Layout from '@theme/Layout';
export default function Home() {
return (
<Layout>
<h1 style={{margin: 20}}>
<Translate description="The homepage main heading">
Welcome to my Docusaurus translated site!
</Translate>
</h1>
</Layout>
);
}
```
### Create a Crowdin project {#create-a-crowdin-project}
Sign up on [Crowdin](https://crowdin.com/), and create a project.
Use English as the source language, and French as the target language.
![Create a Crowdin project with english as source language, and french as target language](/img/crowdin/crowdin-create-project.png)
Your project is created, but it is empty for now. We will upload the files to translate in the next steps.
### Create the Crowdin configuration {#create-the-crowdin-configuration}
This configuration ([doc](https://support.crowdin.com/configuration-file/)) provides a mapping for the Crowdin CLI to understand:
- Where to find the source files to upload (JSON and Markdown)
- Where to download the files after translation (in `i18n/[locale]`)
Create `crowdin.yml` in `website`:
```yml title="crowdin.yml"
project_id: '123456'
api_token_env: CROWDIN_PERSONAL_TOKEN
preserve_hierarchy: true
files:
# JSON translation files
- source: /i18n/en/**/*
translation: /i18n/%two_letters_code%/**/%original_file_name%
# Docs Markdown files
- source: /docs/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%
# Blog Markdown files
- source: /blog/**/*
translation: /i18n/%two_letters_code%/docusaurus-plugin-content-blog/**/%original_file_name%
```
Crowdin has its own syntax for declaring source/translation paths:
- `**/*`: everything in a subfolder
- `%two_letters_code%`: the 2-letters variant of Crowdin target languages (`fr` in our case)
- `**/%original_file_name%`: the translations will preserve the original folder/file hierarchy
:::info
The Crowdin CLI warnings are not always easy to understand.
We advise to:
- change one thing at a time
- re-upload sources after any configuration change
- use paths starting with `/` (`./` does not work)
- avoid fancy globbing patterns like `/docs/**/*.(md|mdx)` (does not work)
:::
#### Access token {#access-token}
The `api_token_env` attribute defines the **env variable name** read by the Crowdin CLI.
You can obtain a `Personal Access Token` on [your personal profile page](https://crowdin.com/settings#api-key).
:::tip
You can keep the default value `CROWDIN_PERSONAL_TOKEN`, and set this environment variable and on your computer and on the CI server to the generated access token.
:::
:::caution
A Personal Access Tokens grant **read-write access to all your Crowdin projects**.
You should **not commit** it, and it may be a good idea to create a dedicated **Crowdin profile for your company** instead of using a personal account.
:::
#### Other configuration fields {#other-configuration-fields}
- `project_id`: can be hardcoded, and is found on `https://crowdin.com/project/<MY_PROJECT_NAME>/settings#api`
- `preserve_hierarchy`: preserve the folder's hierarchy of your docs on Crowdin UI instead of flattening everything
### Install the Crowdin CLI {#install-the-crowdin-cli}
This tutorial uses the CLI version `3.5.2`, but we expect `3.x` releases to keep working.
Install the Crowdin CLI as an npm package to your Docusaurus site:
```bash npm2yarn
npm install @crowdin/cli@3
```
Add a `crowdin` script:
```json title="package.json"
{
"scripts": {
// ...
"write-translations": "docusaurus write-translations",
"crowdin": "crowdin"
}
}
```
Test that you can run the Crowdin CLI:
```bash npm2yarn
npm run crowdin -- --version
```
Set the `CROWDIN_PERSONAL_TOKEN` env variable on your computer, to allow the CLI to authenticate with the Crowdin API.
:::tip
Temporarily, you can hardcode your personal token in `crowdin.yml` with `api_token: 'MY-TOKEN'`.
:::
### Upload the sources {#upload-the-sources}
Generate the JSON translation files for the default language in `website/i18n/en`:
```bash npm2yarn
npm run write-translations
```
Upload all the JSON and Markdown translation files:
```bash npm2yarn
npm run crowdin upload
```
![Crowdin CLI uploading Docusaurus source files](/img/crowdin/crowdin-upload-sources-cli.png)
Your source files are now visible on the Crowdin interface: `https://crowdin.com/project/<MY_PROJECT_NAME>/settings#files`
![Crowdin UI showing Docusaurus source files](/img/crowdin/crowdin-source-files.png)
### Translate the sources {#translate-the-sources}
On `https://crowdin.com/project/<MY_PROJECT_NAME>`, click on the French target language.
![Crowdin UI showing French translation files](/img/crowdin/crowdin-french-translations.png)
Translate some Markdown files.
![Crowdin UI to translate a Markdown file](/img/crowdin/crowdin-translate-markdown.png)
:::tip
Use `Hide String` to make sure translators **don't translate things that should not be**:
- Front matter: `id`, `slug`, `tags` ...
- Admonitions: `:::`, `:::note`, `:::tip` ...
![Crowdin UI hide string](/img/crowdin/crowdin-hide-string.png)
:::
Translate some JSON files.
![Crowdin UI to translate a JSON file](/img/crowdin/crowdin-translate-json.png)
:::info
The `description` attribute of JSON translation files is visible on Crowdin to help translate the strings.
:::
:::tip
**[Pre-translate](https://support.crowdin.com/pre-translation-via-machine/)** your site, and **fix pre-translation mistakes manually** (enable the Global Translation Memory in settings first).
Use the `Hide String` feature first, as Crowdin is pre-translating things too optimistically.
:::
### Download the translations {#download-the-translations}
Use the Crowdin CLI to download the translated JSON and Markdown files.
```bash npm2yarn
npm run crowdin download
```
The translated content should be downloaded in `i18n/fr`.
Start your site on the French locale:
```bash npm2yarn
npm run start -- --locale fr
```
Make sure that your website is now translated in French at [http://localhost:3000/fr/](http://localhost:3000/fr/).
### Automate with CI {#automate-with-ci}
We will configure the CI to **download the Crowdin translations at build time** and keep them outside of Git.
Add `website/i18n` to `.gitignore`.
Set the `CROWDIN_PERSONAL_TOKEN` env variable on your CI.
Create an npm script to `sync` Crowdin (extract sources, upload sources, download translations):
```json title="package.json"
{
"scripts": {
"crowdin:sync": "docusaurus write-translations && crowdin upload && crowdin download"
}
}
```
Call the `npm run crowdin:sync` script in your CI, just before building the Docusaurus site.
:::tip
Keep your deploy-previews fast: don't download translations, and use `npm run build -- --locale en` for feature branches.
:::
:::caution
Crowdin does not support well multiple concurrent uploads/downloads: it is preferable to only include translations to your production deployment, and keep deploy previews untranslated.
:::
## Advanced Crowdin topics {#advanced-crowdin-topics}
### MDX {#mdx}
:::caution
Pay special attention to the JSX fragments in MDX documents!
:::
Crowdin **does not support officially MDX**, but they added **support for the `.mdx` extension**, and interpret such files as Markdown (instead of plain text).
#### MDX problems {#mdx-problems}
Crowdin thinks that the JSX syntax is embedded HTML and can mess up with the JSX markup when you download the translations, leading to a site that fails to build due to invalid JSX.
Simple JSX fragments using simple string props like `<Username name="Sebastien"/>` will work fine; more complex JSX fragments using object/array props like `<User person={{name: "Sebastien"}}/>` are more likely to fail due to a syntax that does not look like HTML.
#### MDX solutions {#mdx-solutions}
We recommend extracting the complex embedded JSX code as separate standalone components. We also added an `mdx-code-block` escape hatch syntax:
`````text
# How to deploy Docusaurus
To deploy Docusaurus, run the following command:
````mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs>
<TabItem value="bash" label="Bash">
```bash
GIT_USER=<GITHUB_USERNAME> yarn deploy
```
</TabItem>
<TabItem value="windows" label="Windows">
```batch
cmd /C "set "GIT_USER=<GITHUB_USERNAME>" && yarn deploy"
```
</TabItem>
</Tabs>
````
`````
This will:
- be interpreted by Crowdin as code blocks (and not mess-up with the markup on download)
- be interpreted by Docusaurus as regular JSX (as if it was not wrapped by any code block)
- unfortunately opt-out of MDX tooling (IDE syntax highlighting, Prettier...)
### Docs versioning {#docs-versioning}
Configure translation files for the `website/versioned_docs` folder.
When creating a new version, the source strings will generally be quite similar to the current version (`website/docs`), and you don't want to translate the new version docs again and again.
Crowdin provides a `Duplicate Strings` setting.
![Crowdin Duplicate Strings option setting](/img/crowdin/crowdin-settings-duplicate-strings.png)
We recommend using `Hide`, but the ideal setting depends on how much your versions are different.
:::caution
Not using `Hide` leads to a much larger amount of `source strings` in quotas, and will affect the pricing.
:::
### Multi-instance plugins {#multi-instance-plugins}
You need to configure translation files for each plugin instance.
If you have a docs plugin instance with `id=ios`, you will need to configure those source files as well
- `website/ios`
- `website/ios_versioned_docs` (if versioned)
### Maintaining your site {#maintaining-your-site}
Sometimes, you will **remove or rename a source file** on Git, and Crowdin will display CLI warnings:
![Crowdin CLI: download translation warning](/img/crowdin/crowdin-download-translations-warning.png)
When your sources are refactored, you should use the Crowdin UI to **update your Crowdin files manually**:
![Crowdin UI: renaming a file](/img/crowdin/crowdin-files-rename.png)
### VCS (Git) integrations {#vcs-git-integrations}
Crowdin has multiple VCS integrations for [GitHub](https://support.crowdin.com/github-integration/), GitLab, Bitbucket.
:::warning TL;DR
We recommend avoiding them.
:::
It could have been helpful to be able to edit the translations in both Git and Crowdin, and have a **bi-directional sync** between the 2 systems.
In practice, **it didn't work very reliably** for a few reasons:
- The Crowdin -> Git sync works fine (with a pull request)
- The Git -> Crowdin sync is manual (you have to press a button)
- The heuristics used by Crowdin to match existing Markdown translations to existing Markdown sources are not 100% reliable, and you have to verify the result on Crowdin UI after any sync from Git
- 2 users concurrently editing on Git and Crowdin can lead to a translation loss
- It requires the `crowdin.yml` file to be at the root of the repository
### In-Context localization {#in-context-localization}
Crowdin has an [In-Context localization](https://support.crowdin.com/in-context-localization/) feature.
:::caution
Unfortunately, it does not work yet for technical reasons, but we have good hope it can be solved.
Crowdin replaces Markdown strings with technical IDs such as `crowdin:id12345`, but it does so too aggressively, including hidden strings, and messes up with front matter, admonitions, JSX...
:::
### Localize edit URLs {#localize-edit-urls}
When the user is browsing a page at `/fr/doc1`, the edit button will link by default to the unlocalized doc at `website/docs/doc1.md`.
You may prefer the edit button to link to the Crowdin interface instead by using the `editUrl` function to customize the edit URLs on a per-locale basis.
```js title="docusaurus.config.js"
const DefaultLocale = 'en';
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// highlight-start
editUrl: ({locale, versionDocsDirPath, docPath}) => {
// Link to Crowdin for French docs
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
// Link to GitHub for English docs
return `https://github.com/facebook/docusaurus/edit/main/website/${versionDocsDirPath}/${docPath}`;
},
// highlight-end
},
blog: {
// highlight-start
editUrl: ({locale, blogDirPath, blogPath}) => {
if (locale !== DefaultLocale) {
return `https://crowdin.com/project/docusaurus-v2/${locale}`;
}
return `https://github.com/facebook/docusaurus/edit/main/website/${blogDirPath}/${blogPath}`;
},
// highlight-start
},
},
],
],
};
```
:::note
It is currently **not possible to link to a specific file** in Crowdin.
:::
### Example configuration {#example-configuration}
The **Docusaurus v2 configuration file** is a good example of using versioning and multi-instance:
```mdx-code-block
import CrowdinConfigV2 from '!!raw-loader!@site/../crowdin-v2.yaml';
import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-yaml" title="crowdin.yml">
{CrowdinConfigV2.split('\n')
// remove comments
.map((line) => !line.startsWith('#') && line)
.filter(Boolean)
.join('\n')}
</CodeBlock>
```

View file

@ -0,0 +1,181 @@
---
id: git
slug: /i18n/git
---
# i18n - Using git
A **possible translation strategy** is to **version control the translation files** with Git (or any other [VCS](https://en.wikipedia.org/wiki/Version_control)).
## Tradeoffs {#tradeoffs}
This strategy has advantages:
- **Easy to get started**: just commit the `i18n` folder to Git
- **Easy for developers**: Git, GitHub and pull requests are mainstream developer tools
- **Free** (or without any additional cost, assuming you already use Git)
- **Low friction**: does not require signing up to an external tool
- **Rewarding**: contributors are happy to have a nice contribution history
Using Git also present some shortcomings:
- **Hard for non-developers**: they do not master Git and pull-requests
- **Hard for professional translators**: they are used to SaaS translation softwares and advanced features
- **Hard to maintain**: you have to keep the translated files **in sync** with the untranslated files
:::note
Some **large-scale technical projects** (React, Vue.js, MDN, TypeScript, Nuxt.js, etc.) use Git for translations.
Refer to the [Docusaurus i18n RFC](https://github.com/facebook/docusaurus/issues/3317) for our notes and links studying these systems.
:::
## Initialization {#initialization}
This is a walk-through of using Git to translate a newly initialized English Docusaurus website into French, and assume you already followed the [i18n tutorial](./i18n-tutorial.mdx).
### Prepare the Docusaurus site {#prepare-the-docusaurus-site}
Initialize a new Docusaurus site:
```bash
npx create-docusaurus@latest website classic
```
Add the site configuration for the French language:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
themeConfig: {
navbar: {
items: [
// ...
{
type: 'localeDropdown',
position: 'left',
},
// ...
],
},
},
// ...
};
```
Translate the homepage:
```jsx title="src/pages/index.js"
import React from 'react';
import Translate from '@docusaurus/Translate';
import Layout from '@theme/Layout';
export default function Home() {
return (
<Layout>
<h1 style={{margin: 20}}>
<Translate description="The homepage main heading">
Welcome to my Docusaurus translated site!
</Translate>
</h1>
</Layout>
);
}
```
### Initialize the `i18n` folder {#initialize-the-i18n-folder}
Use the [write-translations](../cli.mdx#docusaurus-write-translations-sitedir) CLI command to initialize the JSON translation files for the French locale:
```bash npm2yarn
npm run write-translations -- --locale fr
1 translations written at i18n/fr/code.json
11 translations written at i18n/fr/docusaurus-theme-classic/footer.json
4 translations written at i18n/fr/docusaurus-theme-classic/navbar.json
3 translations written at i18n/fr/docusaurus-plugin-content-docs/current.json
```
:::tip
Use the `--messagePrefix '(fr) '` option to make the untranslated strings stand out.
`Hello` will appear as `(fr) Hello` and makes it clear a translation is missing.
:::
Copy your untranslated Markdown files to the French folder:
```bash
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
```
Add all these files to Git.
### Translate the files {#translate-the-files}
Translate the Markdown and JSON files in `i18n/fr` and commit the translation.
You should now be able to start your site in French and see the translations:
```bash npm2yarn
npm run start -- --locale fr
```
You can also build the site locally or on your CI:
```bash npm2yarn
npm run build
# or
npm run build -- --locale fr
```
### Repeat {#repeat}
Follow the same process for each locale you need to support.
## Maintenance {#maintenance}
Keeping translated files **consistent** with the originals **can be challenging**, in particular for Markdown documents.
### Markdown translations {#markdown-translations}
When an untranslated Markdown document is edited, it is **your responsibility to maintain the respective translated files**, and we unfortunately don't have a good way to help you do so.
To keep your translated sites consistent, when the `website/docs/doc1.md` doc is edited, you need **backport these edits** to `i18n/fr/docusaurus-plugin-content-docs/current/doc1.md`.
### JSON translations {#json-translations}
To help you maintain the JSON translation files, it is possible to run again the [write-translations](../cli.mdx#docusaurus-write-translations-sitedir) CLI command:
```bash npm2yarn
npm run write-translations -- --locale fr
```
New translations will be appended, and existing ones will not be overridden.
:::tip
Reset your translations with the `--override` option.
:::
### Localize edit URLs {#localize-edit-urls}
When the user is browsing a page at `/fr/doc1`, the edit button will link by default to the unlocalized doc at `website/docs/doc1.md`.
Your translations are on Git, and you can use the `editLocalizedFiles: true` option of the docs and blog plugins.
The edit button will link to the localized doc at `i18n/fr/docusaurus-plugin-content-docs/current/doc1.md`.

View file

@ -0,0 +1,137 @@
---
id: introduction
slug: /i18n/introduction
---
# i18n - Introduction
It is **easy to translate a Docusaurus website** with its internationalization ([i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization)) support.
## Goals {#goals}
It is important to understand the **design decisions** behind the Docusaurus i18n support.
For more context, you can read the initial [RFC](https://github.com/facebook/docusaurus/issues/3317) and [PR](https://github.com/facebook/docusaurus/pull/3325).
### i18n goals {#i18n-goals}
The goals of the Docusaurus i18n system are:
- **Simple**: just put the translated files in the correct filesystem location
- **Flexible translation workflows**: use Git (monorepo, forks, or submodules), SaaS software, FTP
- **Flexible deployment options**: single, multiple domains, or hybrid
- **Modular**: allow plugin authors to provide i18n support
- **Low-overhead runtime**: documentation is mostly static and does not require heavy JS libraries or polyfills
- **Scalable build-times**: allow building and deploying localized sites independently
- **Localize assets**: an image of your site might contain text that should be translated
- **No coupling**: not forced to use any SaaS, yet integrations are possible
- **Easy to use with [Crowdin](https://crowdin.com/)**: a lot of Docusaurus v1 sites use Crowdin and should be able to migrate to v2
- **Good SEO defaults**: we set useful SEO headers like [`hreflang`](https://developers.google.com/search/docs/advanced/crawling/localized-versions) for you
- **RTL support**: locales reading right-to-left (Arabic, Hebrew, etc.) are supported and easy to implement
- **Default translations**: classic theme labels are translated for you in [many languages](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-translations/locales)
### i18n non-goals {#i18n-non-goals}
We don't provide support for:
- **Automatic locale detection**: opinionated, and best done on the [server (your hosting provider)](../deployment.mdx)
- **Translation SaaS software**: you are responsible to understand the external tools of your choice
- **Translation of slugs**: technically complicated, little SEO value
## Translation workflow {#translation-workflow}
### Overview {#overview}
Overview of the workflow to create a translated Docusaurus website:
1. **Configure**: declare the default locale and alternative locales in `docusaurus.config.js`
2. **Translate**: put the translation files at the correct filesystem location
3. **Deploy**: build and deploy your site using a single or multi-domain strategy
### Translation files {#translation-files}
You will work with three kinds of translation files.
#### Markdown files {#markdown-files}
This is the main content of your Docusaurus website.
Markdown and MDX documents are translated as a whole, to fully preserve the translation context, instead of splitting each sentence as a separate string.
#### JSON files {#json-files}
JSON is used to translate:
- Your React code: standalone React pages in `src/pages`, or other components
- Layout labels provided through `themeConfig`: navbar, footer
- Layout labels provided through plugin options: docs sidebar category labels, blog sidebar title...
The JSON format used is called **Chrome i18n**:
```json
{
"myTranslationKey1": {
"message": "Translated message 1",
"description": "myTranslationKey1 is used on the homepage"
},
"myTranslationKey2": {
"message": "Translated message 2",
"description": "myTranslationKey2 is used on the FAQ page"
}
}
```
The choice was made for 2 reasons:
- **Description attribute**: to help translators with additional context
- **Widely supported**: [Chrome extensions](https://developer.chrome.com/docs/extensions/mv2/i18n-messages/), [Crowdin](https://support.crowdin.com/file-formats/chrome-json/), [Transifex](https://docs.transifex.com/formats/chrome-json), [Phrase](https://help.phrase.com/help/chrome-json-messages), [Applanga](https://www.applanga.com/docs/formats/chrome_i18n_json), etc.
#### Data files {#data-files}
Some plugins may read from external data files that are localized as a whole. For example, the blog plugin uses an [`authors.yml`](../blog.mdx#global-authors) file that can be translated by creating a copy under `i18n/[locale]/docusaurus-plugin-content-blog/authors.yml`.
### Translation files location {#translation-files-location}
The translation files should be created at the correct filesystem location.
Each locale and plugin has its own `i18n` subfolder:
```
website/i18n/[locale]/[pluginName]/...
```
:::note
For multi-instance plugins, the path is `website/i18n/[locale]/[pluginName]-[pluginId]/...`.
:::
Translating a very simple Docusaurus site in French would lead to the following tree:
```bash
website/i18n
└── fr
├── code.json # Any text label present in the React code
│ # Includes text labels from the themes' code
├── docusaurus-plugin-content-blog # translation data the blog plugin needs
│ └── 2020-01-01-hello.md
├── docusaurus-plugin-content-docs # translation data the docs plugin needs
│ ├── current
│ │ ├── doc1.md
│ │ └── doc2.mdx
│ └── current.json
└── docusaurus-theme-classic # translation data the classic theme needs
├── footer.json # Text labels in your footer theme config
└── navbar.json # Text labels in your navbar theme config
```
The JSON files are initialized with the [`docusaurus write-translations`](../cli.mdx#docusaurus-write-translations-sitedir) CLI command. Each plugin sources its own translated content under the corresponding folder, while the `code.json` file defines all text labels used in the React code.
Each content plugin or theme is different, and **defines its own translation files location**:
- [Docs i18n](../api/plugins/plugin-content-docs.mdx#i18n)
- [Blog i18n](../api/plugins/plugin-content-blog.mdx#i18n)
- [Pages i18n](../api/plugins/plugin-content-pages.mdx#i18n)
- [Themes i18n](../api/themes/theme-configuration.mdx#i18n)

View file

@ -0,0 +1,517 @@
---
id: tutorial
description: This tutorial will walk you through the basics of the Docusaurus i18n system.
slug: /i18n/tutorial
---
# i18n - Tutorial
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
This tutorial will walk you through the basics of the **Docusaurus i18n system**.
We will add **French** translations to a **newly initialized English Docusaurus website**.
Initialize a new site with `npx create-docusaurus@latest website classic` (like [this one](https://github.com/facebook/docusaurus/tree/main/examples/classic)).
## Configure your site {#configure-your-site}
Modify `docusaurus.config.js` to add the i18n support for the French language.
### Site configuration {#site-configuration}
Use the [site i18n configuration](./../api/docusaurus.config.js.mdx#i18n) to declare the i18n locales:
```js title="docusaurus.config.js"
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr', 'fa'],
localeConfigs: {
en: {
htmlLang: 'en-GB',
},
// You can omit a locale (e.g. fr) if you don't need to override the defaults
fa: {
direction: 'rtl',
},
},
},
};
```
The locale names are used for the translation files' locations, as well as your translated locales' base URL. When building all locales, only the default locale will have its name omitted in the base URL.
Docusaurus uses the locale names to provide **sensible defaults**: the `<html lang="...">` attribute, locale label, calendar format, etc. You can customize these defaults with the `localeConfigs`.
### Theme configuration {#theme-configuration}
Add a **navbar item** of type `localeDropdown` so that users can select the locale they want:
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
navbar: {
items: [
// highlight-start
{
type: 'localeDropdown',
position: 'left',
},
// highlight-end
],
},
},
};
```
### Start your site {#start-your-site}
Start your localized site in dev mode, using the locale of your choice:
```bash npm2yarn
npm run start -- --locale fr
```
Your site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/).
We haven't provided any translation yet, so the site is mostly untranslated.
:::tip
Docusaurus provides **default translations** for generic theme labels, such as "Next" and "Previous" for the pagination.
Please help us complete those **[default translations](https://github.com/facebook/docusaurus/tree/main/packages/docusaurus-theme-translations/locales)**.
:::
:::caution
Each locale is a **distinct standalone single-page application**: it is not possible to start the Docusaurus sites in all locales at the same time.
:::
## Translate your site {#translate-your-site}
All translation data for the French locale is stored in `website/i18n/fr`. Each plugin sources its own translated content under the corresponding folder, while the `code.json` file defines all text labels used in the React code.
:::note
After copying files around, restart your site with `npm run start -- --locale fr`. Hot-reload will work better when editing existing files.
:::
### Translate your React code {#translate-your-react-code}
For any React code you've written yourself: React pages, React components, etc., you will use the [**translation APIs**](../docusaurus-core.mdx#translate).
Locate all text labels in your React code that will be visible to your users, and mark them with the translation APIs. There are two kinds of APIs:
- The `<Translate>` component wraps a string as a JSX element;
- The `translate()` callback takes a message and returns a string.
Use the one that better fits the context semantically. For example, the `<Translate>` can be used as React children, while for props that expect a string, the callback can be used.
:::caution
A JSX element is an _object_, not a string. Using it in contexts expecting strings (such as the children of [`<option>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/option)) would [coerce it to a string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString), which returns `"[object Object]"`. While we encourage you to use `<Translate>` as JSX children, only use the element form when it actually works.
:::
```mdx-code-block
<Tabs>
<TabItem value="Before">
```
```jsx title="src/pages/index.js"
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
export default function Home() {
return (
<Layout>
{/* highlight-next-line */}
<h1>Welcome to my website</h1>
<main>
{/* highlight-start */}
You can also visit my
<Link to="https://docusaurus.io/blog">blog</Link>
{/* highlight-end */}
<img
src="/img/home.png"
// highlight-next-line
alt="Home icon"
/>
</main>
</Layout>
);
}
```
```mdx-code-block
</TabItem>
<TabItem value="After">
```
```jsx title="src/pages/index.js"
import React from 'react';
import Layout from '@theme/Layout';
import Link from '@docusaurus/Link';
// highlight-next-line
import Translate, {translate} from '@docusaurus/Translate';
export default function Home() {
return (
<Layout>
<h1>
{/* highlight-next-line */}
<Translate>Welcome to my website</Translate>
</h1>
<main>
{/* highlight-start */}
<Translate
id="homepage.visitMyBlog"
description="The homepage message to ask the user to visit my blog"
values={{
blogLink: (
<Link to="https://docusaurus.io/blog">
<Translate
id="homepage.visitMyBlog.linkLabel"
description="The label for the link to my blog">
blog
</Translate>
</Link>
),
}}>
{'You can also visit my {blogLink}'}
</Translate>
{/* highlight-end */}
<img
src="/img/home.png"
alt={
// highlight-start
translate({
message: 'Home icon',
description: 'The homepage icon alt message',
})
// highlight-end
}
/>
</main>
</Layout>
);
}
```
```mdx-code-block
</TabItem>
</Tabs>
```
:::info
Docusaurus provides a **very small and lightweight translation runtime** on purpose, and only supports basic [placeholders interpolation](../docusaurus-core.mdx#interpolate), using a subset of the [ICU Message Format](https://formatjs.io/docs/core-concepts/icu-syntax/).
Most documentation websites are generally **static** and don't need advanced i18n features (**plurals**, **genders**, etc.). Use a library like [react-intl](https://www.npmjs.com/package/react-intl) for more advanced use-cases.
:::
The `docusaurus write-translations` command will statically analyze all React code files used in your site, extract calls to these APIs, and aggregate them in the `code.json` file. The translation files will be stored as maps from IDs to translation message objects (including the translated label and the description of the label). In your calls to the translation APIs (`<Translate>` or `translate()`), you need to specify either the default untranslated message or the ID, in order for Docusaurus to correctly correlate each translation entry to the API call.
:::caution text labels must be static
The `docusaurus write-translations` command only does **static analysis** of your code. It doesn't actually run your site. Therefore, dynamic messages can't be extracted, as the message is an _expression_, not a _string_:
```jsx
const items = [
{id: 1, title: 'Hello'},
{id: 2, title: 'World'},
];
function ItemsList() {
return (
<ul>
{/* DON'T DO THIS: doesn't work with the write-translations command */}
{items.map((item) => (
<li key={item.id}>
<Translate>{item.title}</Translate>
</li>
))}
<ul>
);
}
```
This still behaves correctly at runtime. However, in the future, we may provide a "no-runtime" mechanism, allowing the translations to be directly inlined in the React code through Babel transformations, instead of calling the APIs at runtime. Therefore, to be future-proof, you should always prefer statically analyzable messages. For example, we can refactor the code above to:
```jsx
const items = [
{id: 1, title: <Translate>Hello</Translate>},
{id: 2, title: <Translate>World</Translate>},
];
function ItemsList() {
return (
<ul>
{/* The titles are now already translated when rendering! */}
{items.map((item) => (
<li key={item.id}>{item.title}</li>
))}
<ul>
);
}
```
You can see the calls to the translation APIs as purely _markers_ that tell Docusaurus that "here's a text label to be replaced with a translated message".
:::
#### Pluralization {#pluralization}
When you run `write-translations`, you will notice that some labels are pluralized:
```json title="i18n/en/code.json"
{
// ...
"theme.blog.post.plurals": "One post|{count} posts"
// ...
}
```
Every language will have a list of [possible plural categories](https://unicode-org.github.io/cldr-staging/charts/37/supplemental/language_plural_rules.html). Docusaurus will arrange them in the order of `["zero", "one", "two", "few", "many", "other"]`. For example, because English (`en`) has two plural forms ("one" and "other"), the translation message has two labels separated by a pipe (`|`). For Polish (`pl`) which has three plural forms ("one", "few", and "many"), you would provide three labels in that order, joined by pipes.
You can pluralize your own code's messages as well:
```jsx
import {translate} from '@docusaurus/Translate';
import {usePluralForm} from '@docusaurus/theme-common';
function ItemsList({items}) {
// `usePluralForm` will provide the plural selector for the current locale
const {selectMessage} = usePluralForm();
// Select the appropriate pluralized label based on `items.length`
const message = selectMessage(
items.length,
translate(
{message: 'One item|{count} items'},
{count: items.length},
),
);
return (
<>
<h2>{message}</h2>
<ul>{items.map((item) => <li key={item.id}>{item.title}</li>)}<ul>
</>
);
}
```
:::note
Docusaurus uses [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) to resolve and select plural forms. It is important to provide the right number of plural forms in the right order for `selectMessage` to work.
:::
### Translate plugin data {#translate-plugin-data}
JSON translation files are used for everything that is interspersed in your code:
- React code, including the translated labels you have marked above
- Navbar and footer labels in theme config
- Docs sidebar category labels in `sidebars.js`
- Blog sidebar title in plugin options
- ...
Run the [write-translations](../cli.mdx#docusaurus-write-translations-sitedir) command:
```bash npm2yarn
npm run write-translations -- --locale fr
```
It will extract and initialize the JSON translation files that you need to translate. The `code.json` file at the root includes all translation API calls extracted from the source code, which could either be written by you or provided by the themes, some of which may already be translated by default.
```json title="i18n/fr/code.json"
{
// No ID for the <Translate> component: the default message is used as ID
"Welcome to my website": {
"message": "Welcome to my website"
},
"home.visitMyBlog": {
"message": "You can also visit my {blog}",
"description": "The homepage message to ask the user to visit my blog"
},
"homepage.visitMyBlog.linkLabel": {
"message": "Blog",
"description": "The label for the link to my blog"
},
"Home icon": {
"message": "Home icon",
"description": "The homepage icon alt message"
}
}
```
Plugins and themes will also write their own JSON translation files, such as:
```json title="i18n/fr/docusaurus-theme-classic/navbar.json"
{
"title": {
"message": "My Site",
"description": "The title in the navbar"
},
"item.label.Docs": {
"message": "Docs",
"description": "Navbar item with label Docs"
},
"item.label.Blog": {
"message": "Blog",
"description": "Navbar item with label Blog"
},
"item.label.GitHub": {
"message": "GitHub",
"description": "Navbar item with label GitHub"
}
}
```
Translate the `message` attribute in the JSON files of `i18n/fr`, and your site layout and homepage should now be translated.
### Translate Markdown files {#translate-markdown-files}
Official Docusaurus content plugins extensively use Markdown/MDX files and allow you to translate them.
#### Translate the docs {#translate-the-docs}
Copy your docs Markdown files from `docs/` to `i18n/fr/docusaurus-plugin-content-docs/current`, and translate them:
```bash
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current
cp -r docs/** i18n/fr/docusaurus-plugin-content-docs/current
```
:::info
Notice that the `docusaurus-plugin-content-docs` plugin always divides its content by versions. The data in `./docs` folder will be translated in the `current` subfolder and `current.json` file. See [the doc versioning guide](../guides/docs/versioning.mdx#terminology) for more information about what "current" means.
:::
#### Translate the blog {#translate-the-blog}
Copy your blog Markdown files to `i18n/fr/docusaurus-plugin-content-blog`, and translate them:
```bash
mkdir -p i18n/fr/docusaurus-plugin-content-blog
cp -r blog/** i18n/fr/docusaurus-plugin-content-blog
```
#### Translate the pages {#translate-the-pages}
Copy your pages Markdown files to `i18n/fr/docusaurus-plugin-content-pages`, and translate them:
```bash
mkdir -p i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.md i18n/fr/docusaurus-plugin-content-pages
cp -r src/pages/**.mdx i18n/fr/docusaurus-plugin-content-pages
```
:::caution
We only copy `.md` and `.mdx` files, as React pages are translated through JSON translation files already.
:::
:::tip Use explicit heading IDs
By default, a Markdown heading `### Hello World` will have a generated ID `hello-world`. Other documents can link it with `[link](#hello-world)`. However, after translation, the heading becomes `### Bonjour le Monde`, with ID `bonjour-le-monde`.
Generated IDs are not always a good fit for localized sites, as it requires you to localize all the anchor links:
```diff
- [link](#hello-world).
+ [link](#bonjour-le-monde)
```
For localized sites, it is recommended to use **[explicit heading IDs](../guides/markdown-features/markdown-features-toc.mdx#explicit-ids)**.
:::
## Deploy your site {#deploy-your-site}
You can choose to deploy your site under a **single domain** or use **multiple (sub)domains**.
### Single-domain deployment {#single-domain-deployment}
Run the following command:
```bash npm2yarn
npm run build
```
Docusaurus will build **one single-page application per locale**:
- `website/build`: for the default, English language
- `website/build/fr`: for the French language
You can now [deploy](../deployment.mdx) the `build` folder to the static hosting solution of your choice.
:::note
The Docusaurus v2 website uses this strategy:
- [https://docusaurus.io](https://docusaurus.io)
- [https://docusaurus.io/fr](https://docusaurus.io/fr)
:::
:::tip
Static hosting providers generally redirect `/unknown/url` to `/404.html` by convention, always showing an **English 404 page**.
**Localize your 404 pages** by configuring your host to redirect `/fr/*` to `/fr/404.html`.
This is not always possible, and depends on your host: GitHub Pages can't do this, [Netlify](https://docs.netlify.com/routing/redirects/redirect-options/#custom-404-page-handling) can.
:::
### Multi-domain deployment {#multi-domain-deployment}
You can also build your site for a single locale:
```bash npm2yarn
npm run build -- --locale fr
```
Docusaurus will not add the `/fr/` URL prefix.
On your [static hosting provider](../deployment.mdx):
- create one deployment per locale
- configure the appropriate build command, using the `--locale` option
- configure the (sub)domain of your choice for each deployment
:::caution
This strategy is **not possible** with GitHub Pages, as it is only possible to **have a single deployment**.
:::
### Hybrid {#hybrid}
It is possible to have some locales using sub-paths, and others using subdomains.
It is also possible to deploy each locale as a separate subdomain, assemble the subdomains in a single unified domain at the CDN level:
- Deploy your site as `fr.docusaurus.io`
- Configure a CDN to serve it from `docusaurus.io/fr`
## Managing translations {#managing-translations}
Docusaurus doesn't care about how you manage your translations: all it needs is that all translation files (JSON, Markdown, or other data files) are available in the file system during building. However, as site creators, you would need to consider how translations are managed so your translation contributors could collaborate well.
We will share two common translation collaboration strategies: [**using git**](./i18n-git.mdx) and [**using Crowdin**](./i18n-crowdin.mdx).

View file

@ -0,0 +1,188 @@
---
description: How to install Docusaurus locally, and start a Docusaurus site in no time.
---
# Installation
```mdx-code-block
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
```
Docusaurus is essentially a set of npm [packages](https://github.com/facebook/docusaurus/tree/main/packages).
:::tip
Use the **[Fast Track](introduction.mdx#fast-track)** to understand Docusaurus in **5 minutes ⏱**!
Use **[docusaurus.new](https://docusaurus.new)** to test Docusaurus immediately in your browser!
:::
## Requirements {#requirements}
- [Node.js](https://nodejs.org/en/download/) version 16.14 or above (which can be checked by running `node -v`). You can use [nvm](https://github.com/nvm-sh/nvm) for managing multiple Node versions on a single machine installed.
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
## Scaffold project website {#scaffold-project-website}
The easiest way to install Docusaurus is to use the command line tool that helps you scaffold a skeleton Docusaurus website. You can run this command anywhere in a new empty repository or within an existing repository, it will create a new directory containing the scaffolded files.
```bash
npx create-docusaurus@latest my-website classic
```
We recommend the `classic` template so that you can get started quickly, and it contains features found in Docusaurus 1. The `classic` template contains `@docusaurus/preset-classic` which includes standard documentation, a blog, custom pages, and a CSS framework (with dark mode support). You can get up and running extremely quickly with the classic template and customize things later on when you have gained more familiarity with Docusaurus.
You can also use the template's TypeScript variant by passing the `--typescript` flag. See [TypeScript support](./typescript-support.mdx) for more information.
```bash
npx create-docusaurus@latest my-website classic --typescript
```
:::info FB-Only
If you are setting up a new Docusaurus website for a Facebook open source project, use the `facebook` template instead, which comes with some useful Facebook-specific defaults:
```bash
npx create-docusaurus@latest my-website facebook
```
:::
<details>
<summary>Alternative installation commands</summary>
You can also initialize a new project using your preferred project manager:
```bash npm2yarn
npm init docusaurus
```
</details>
Run `npx create-docusaurus@latest --help`, or check out its [API docs](./api/misc/create-docusaurus.mdx) for more information about all available flags.
## Project structure {#project-structure}
Assuming you chose the classic template and named your site `my-website`, you will see the following files generated under a new directory `my-website/`:
```bash
my-website
├── blog
│ ├── 2019-05-28-hola.md
│ ├── 2019-05-29-hello-world.md
│ └── 2020-05-30-welcome.md
├── docs
│ ├── doc1.md
│ ├── doc2.md
│ ├── doc3.md
│ └── mdx.md
├── src
│ ├── css
│ │ └── custom.css
│ └── pages
│ ├── styles.module.css
│ └── index.js
├── static
│ └── img
├── docusaurus.config.js
├── package.json
├── README.md
├── sidebars.js
└── yarn.lock
```
### Project structure rundown {#project-structure-rundown}
- `/blog/` - Contains the blog Markdown files. You can delete the directory if you've disabled the blog plugin, or you can change its name after setting the `path` option. More details can be found in the [blog guide](blog.mdx)
- `/docs/` - Contains the Markdown files for the docs. Customize the order of the docs sidebar in `sidebars.js`. You can delete the directory if you've disabled the docs plugin, or you can change its name after setting the `path` option. More details can be found in the [docs guide](./guides/docs/docs-introduction.mdx)
- `/src/` - Non-documentation files like pages or custom React components. You don't have to strictly put your non-documentation files here, but putting them under a centralized directory makes it easier to specify in case you need to do some sort of linting/processing
- `/src/pages` - Any JSX/TSX/MDX file within this directory will be converted into a website page. More details can be found in the [pages guide](guides/creating-pages.mdx)
- `/static/` - Static directory. Any contents inside here will be copied into the root of the final `build` directory
- `/docusaurus.config.js` - A config file containing the site configuration. This is the equivalent of `siteConfig.js` in Docusaurus v1
- `/package.json` - A Docusaurus website is a React app. You can install and use any npm packages you like in them
- `/sidebars.js` - Used by the documentation to specify the order of documents in the sidebar
### Monorepos {#monorepos}
If you are using Docusaurus for documentation of an existing project, a monorepo may be the solution for you. Monorepos allow you to share dependencies between similar projects. For example, your website may use your local packages to showcase the latest features, instead of depending on a released version; your contributors can also conveniently update the docs as they implement features. An example monorepo folder structure is below:
```bash
my-monorepo
├── package-a # Another package, your actual project
│ ├── src
│ └── package.json # Package A's dependencies
├── website # Docusaurus root
│ ├── docs
│ ├── src
│ └── package.json # Docusaurus' dependencies
├── package.json # Monorepo's shared dependencies
```
In this case, you should run `npx create-docusaurus` within the `./my-monorepo` folder.
If you're using a hosting provider such as Netlify or Vercel, you will need to change the `Base directory` of the site to where your Docusaurus root is. In this case, that would be `./website`. Read more about configuring ignore commands in the [deployment docs](./deployment.mdx#deploying-to-netlify).
Read more about monorepos in the [Yarn documentation](https://yarnpkg.com/features/workspaces) (Yarn is not the only way to set up a monorepo, but it's a common solution), or checkout [Docusaurus](https://github.com/facebook/docusaurus) and [Jest](https://github.com/facebook/jest) for some real-world examples.
## Running the development server {#running-the-development-server}
To preview your changes as you edit the files, you can run a local development server that will serve your website and reflect the latest changes.
```bash npm2yarn
cd my-website
npm run start
```
By default, a browser window will open at [http://localhost:3000](http://localhost:3000).
Congratulations! You have just created your first Docusaurus site! Browse around the site to see what's available.
## Build {#build}
Docusaurus is a modern static website generator so we need to build the website into a directory of static contents and put it on a web server so that it can be viewed. To build the website:
```bash npm2yarn
npm run build
```
and contents will be generated within the `/build` directory, which can be copied to any static file hosting service like [GitHub pages](https://pages.github.com/), [Vercel](https://vercel.com/) or [Netlify](https://www.netlify.com/). Check out the docs on [deployment](deployment.mdx) for more details.
## Updating your Docusaurus version {#updating-your-docusaurus-version}
There are many ways to update your Docusaurus version. One guaranteed way is to manually change the version number in `package.json` to the desired version. Note that all `@docusaurus/`-namespaced packages should be using the same version.
import UpgradeGuide from '@site/src/components/UpgradeGuide';
<UpgradeGuide />
Then, in the directory containing `package.json`, run your package manager's install command:
```bash npm2yarn
npm install
```
To check that the update occurred successfully, run:
```bash
npx docusaurus --version
```
You should see the correct version as output.
Alternatively, if you are using Yarn, you can do:
```bash
yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest
```
:::tip
Use new unreleased features of Docusaurus with the [`@canary` npm dist tag](/community/canary)
:::
## Problems? {#problems}
Ask for help on [Stack Overflow](https://stackoverflow.com/questions/tagged/docusaurus), on our [GitHub repository](https://github.com/facebook/docusaurus), our [Discord server](https://discordapp.com/invite/docusaurus), or [Twitter](https://twitter.com/docusaurus).

View file

@ -0,0 +1,205 @@
---
description: Docusaurus was designed from the ground up to be easily installed and used to get your website up and running quickly.
slug: /
---
# Introduction
⚡️ Docusaurus will help you ship a **beautiful documentation site in no time**.
💸 Building a custom tech stack is expensive. Instead, **focus on your content** and just write Markdown files.
💥 Ready for more? Use **advanced features** like versioning, i18n, search and theme customizations.
💅 Check the **[best Docusaurus sites](/showcase?tags=favorite)** for inspiration and read some **[testimonials](https://twitter.com/sebastienlorber/timelines/1392048416872706049)**.
🧐 Docusaurus is a **static-site generator**. It builds a **single-page application** with fast client-side navigation, leveraging the full power of **React** to make your site interactive. It provides out-of-the-box **documentation features** but can be used to create **any kind of site** (personal website, product, blog, marketing landing pages, etc).
![](/img/slash-introducing.svg)
## Fast Track ⏱️ {#fast-track}
Understand Docusaurus in **5 minutes** by playing!
Create a new Docusaurus site and follow the **very short** embedded tutorial.
Install [Node.js](https://nodejs.org/en/download/) and create a new Docusaurus site:
```bash
npx create-docusaurus@latest my-website classic
```
Start the site:
```bash
cd my-website
npx docusaurus start
```
Open [http://localhost:3000](http://localhost:3000) and follow the tutorial.
:::tip
Use **[docusaurus.new](https://docusaurus.new)** to test Docusaurus immediately in your browser!
Or read the **[5-minute tutorial](https://tutorial.docusaurus.io)** online.
:::
## Docusaurus: Documentation Made Easy
In this presentation at [Algolia Community Event](https://www.algolia.com/), [Meta Open Source team](https://opensource.facebook.com/) shared a brief walk-through of Docusaurus. They covered how to get started with the project, enable plugins, and set up functionalities like documentation and blogging.
```mdx-code-block
import LiteYouTubeEmbed from 'react-lite-youtube-embed';
<div className="video-container">
<LiteYouTubeEmbed
// cSpell:ignore Yhyx Sksg
id="Yhyx7otSksg"
params="autoplay=1&autohide=1&showinfo=0&rel=0"
title="Docusaurus: Documentation Made Easy"
poster="maxresdefault"
webp
/>
</div>
```
## Migrating from v1 {#migrating-from-v1}
Docusaurus v2 has been a total rewrite from Docusaurus v1, taking advantage of a completely modernized toolchain. After [v2's official release](https://docusaurus.io/blog/2022/08/01/announcing-docusaurus-2.0), we highly encourage you to **use Docusaurus v2 over Docusaurus v1**, as Docusaurus v1 has been deprecated.
A [lot of users](/showcase) are already using Docusaurus v2 ([trends](https://www.npmtrends.com/docusaurus-vs-@docusaurus/core)).
**Use Docusaurus v2 if:**
- :white_check_mark: You want a modern Jamstack documentation site
- :white_check_mark: You want a single-page application (SPA) with client-side routing
- :white_check_mark: You want the full power of React and MDX
- :white_check_mark: You do not need support for IE11
**Use [Docusaurus v1](https://v1.docusaurus.io/) if:**
- :x: You don't want a single-page application (SPA)
- :x: You need support for IE11 (...do you? IE [has already reached end-of-life](https://docs.microsoft.com/en-us/lifecycle/products/internet-explorer-11) and is no longer officially supported)
For existing v1 users that are seeking to upgrade to v2, you can follow our [migration guide](./migration/migration-overview.mdx).
## Features {#features}
Docusaurus is built with high attention to the developer and contributor experience.
- ⚛️ **Built with 💚 and React**:
- Extend and customize with React
- Gain full control of your site's browsing experience by providing your own React components
- **Pluggable**:
- Bootstrap your site with a basic template, then use advanced features and plugins
- Open source your plugins to share with the community
- ✂️ **Developer experience**:
- Start writing your docs right now
- Universal configuration entry point to make it more maintainable by contributors
- Hot reloading with lightning-fast incremental build on changes
- Route-based code and data splitting
- Publish to GitHub Pages, Netlify, Vercel, and other deployment services with ease
Our shared goal—to help your users quickly find what they need and understand your products better. We share our best practices to help you build your docs site right and well.
- 🎯 **SEO friendly**:
- HTML files are statically generated for every possible path.
- Page-specific SEO to help your users land on your official docs directly relating their problems at hand.
- 📝 **Powered by MDX**:
- Write interactive components via JSX and React embedded in Markdown.
- Share your code in live editors to get your users to love your products on the spot.
- 🔍 **Search**: Your full site is searchable.
- 💾 **Document Versioning**: Helps you keep documentation in sync with project releases.
- 🌍 **Internationalization (i18n)**: Translate your site in multiple locales.
Docusaurus 2 is born to be compassionately accessible to all your users, and lightning-fast.
- ⚡️ **Lightning-fast**. Docusaurus 2 follows the [PRPL Pattern](https://developers.google.com/web/fundamentals/performance/prpl-pattern/) that makes sure your content loads blazing fast.
- 🦖 **Accessible**. Attention to accessibility, making your site equally accessible to all users.
## Design principles {#design-principles}
- **Little to learn**. Docusaurus should be easy to learn and use as the API is quite small. Most things will still be achievable by users, even if it takes them more code and more time to write. Not having abstractions is better than having the wrong abstractions, and we don't want users to have to hack around the wrong abstractions. Mandatory talk—[Minimal API Surface Area](https://www.youtube.com/watch?v=4anAwXYqLG8).
- **Intuitive**. Users will not feel overwhelmed when looking at the project directory of a Docusaurus project or adding new features. It should look intuitive and easy to build on top of, using approaches they are familiar with.
- **Layered architecture**. The separations of concerns between each layer of our stack (content/theming/styling) should be clear—well-abstracted and modular.
- **Sensible defaults**. Common and popular performance optimizations and configurations will be done for users but they are given the option to override them.
- **No vendor lock-in**. Users are not required to use the default plugins or CSS, although they are highly encouraged to. Certain core infrastructures like React Loadable and React Router cannot be swapped because we do default performance optimization on them, but not higher-level ones. Choice of Markdown engines, CSS frameworks, CSS methodology, and other architectures will be entirely up to users.
We believe that, as developers, knowing how a library works helps us become better at using it. Hence we're dedicating effort to explaining the architecture and various components of Docusaurus with the hope that users reading it will gain a deeper understanding of the tool and be even more proficient in using it.
<!--
Explain the principles that guide the development of Docusaurus.
References
---
- https://www.gatsbyjs.org/docs/behind-the-scenes/
- https://reactjs.org/docs/design-principles.html
- https://v1.vuepress.vuejs.org/miscellaneous/design-concepts.html
-->
## Comparison with other tools {#comparison-with-other-tools}
Across all static site generators, Docusaurus has a unique focus on documentation sites and has many out-of-the-box features.
We've also studied other main static site generators and would like to share our insights on the comparison, hopefully helping you navigate through the prismatic choices out there.
### Gatsby {#gatsby}
[Gatsby](https://www.gatsbyjs.com/) is packed with a lot of features, has a rich ecosystem of plugins, and is capable of doing everything that Docusaurus does. Naturally, that comes at a cost of a higher learning curve. Gatsby does many things well and is suitable for building many types of websites. On the other hand, Docusaurus tries to do one thing super well - be the best tool for writing and publishing content.
GraphQL is also pretty core to Gatsby, although you don't necessarily need GraphQL to build a Gatsby site. In most cases when building static websites, you won't need the flexibility that GraphQL provides.
Many aspects of Docusaurus 2 were inspired by the best things about Gatsby and it's a great alternative.
[Docz](https://github.com/pedronauck/docz) is a Gatsby theme to build documentation websites. It is currently less featured than Docusaurus.
### Next.js {#nextjs}
[Next.js](https://nextjs.org/) is another very popular hybrid React framework. It can help you build a good documentation website, but it is not opinionated toward the documentation use-case, and it will require a lot more work to implement what Docusaurus provides out-of-the-box.
[Nextra](https://github.com/shuding/nextra) is an opinionated static site generator built on top of Next.js. It is currently less featured than Docusaurus.
### VuePress {#vuepress}
[VuePress](https://vuepress.vuejs.org/) has many similarities with Docusaurus - both focus heavily on content-centric website and provides tailored documentation features out of the box. However, VuePress is powered by Vue, while Docusaurus is powered by React. If you want a Vue-based solution, VuePress would be a decent choice.
### MkDocs {#mkdocs}
[MkDocs](https://www.mkdocs.org/) is a popular Python static site generator with value propositions similar to Docusaurus.
It is a good option if you don't need a single-page application and don't plan to leverage React.
[Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) is a beautiful theme.
### Docsify {#docsify}
[Docsify](https://docsify.js.org/) makes it easy to create a documentation website, but is not a static-site generator and is not SEO friendly.
### GitBook {#gitbook}
[GitBook](https://www.gitbook.com/) has a very clean design and has been used by many open source projects. With its focus shifting towards a commercial product rather than an open-source tool, many of its requirements no longer fit the needs of open source projects' documentation sites. As a result, many have turned to other products. You may read about Redux's switch to Docusaurus [here](https://github.com/reduxjs/redux/issues/3161).
Currently, GitBook is only free for open-source and non-profit teams. Docusaurus is free for everyone.
### Jekyll {#jekyll}
[Jekyll](https://github.com/jekyll/jekyll) is one of the most mature static site generators around and has been a great tool to use — in fact, before Docusaurus, most of Facebook's Open Source websites are/were built on Jekyll! It is extremely simple to get started. We want to bring a similar developer experience as building a static site with Jekyll.
In comparison with statically generated HTML and interactivity added using `<script />` tags, Docusaurus sites are React apps. Using modern JavaScript ecosystem tooling, we hope to set new standards on doc sites' performance, asset building pipeline and optimizations, and ease to set up.
## Staying informed {#staying-informed}
- [GitHub](https://github.com/facebook/docusaurus)
- [Twitter](https://twitter.com/docusaurus)
- [Blog](/blog)
- [Discord](https://discord.gg/docusaurus)
## Something missing? {#something-missing}
If you find issues with the documentation or have suggestions on how to improve the documentation or the project in general, please [file an issue](https://github.com/facebook/docusaurus) for us, or send a tweet mentioning the [@docusaurus](https://twitter.com/docusaurus) Twitter account.
For new feature requests, you can create a post on our [feature requests board (Canny)](/feature-requests), which is a handy tool for road-mapping and allows for sorting by upvotes, which gives the core team a better indicator of what features are in high demand, as compared to GitHub issues which are harder to triage. Refrain from making a Pull Request for new features (especially large ones) as someone might already be working on it or will be part of our roadmap. Talk to us first!

View file

@ -0,0 +1,75 @@
---
slug: /migration/automated
---
# Automated migration
The migration CLI automatically migrates your v1 website to a v2 website.
:::info
Manual work is still required after using the migration CLI, as we can't automate a full migration
:::
The migration CLI migrates:
- Site configurations (from `siteConfig.js` to `docusaurus.config.js`)
- `package.json`
- `sidebars.json`
- `/docs`
- `/blog`
- `/static`
- `versioned_sidebar.json` and `/versioned_docs` if your site uses versioning
To use the migration CLI, follow these steps:
1. Before using the migration CLI, ensure that `/docs`, `/blog`, `/static`, `sidebars.json`, `siteConfig.js`, `package.json` follow the [structure](#) shown at the start of this page.
2. To migrate your v1 website, run the migration CLI with the appropriate filesystem paths:
```bash
# migration command format
npx @docusaurus/migrate migrate <v1 website directory> <desired v2 website directory>
# example
npx @docusaurus/migrate migrate ./v1-website ./v2-website
```
3. To view your new website locally, go into your v2 website's directory and start your development server.
```bash npm2yarn
cd ./v2-website
npm install
npm start
```
:::danger
The migration CLI updates existing files. Be sure to have committed them first!
:::
#### Options {#options}
You can add option flags to the migration CLI to automatically migrate Markdown content and pages to v2. It is likely that you will still need to make some manual changes to achieve your desired result.
| Name | Description |
| -------- | ------------------------------------------------------ |
| `--mdx` | Add this flag to convert Markdown to MDX automatically |
| `--page` | Add this flag to migrate pages automatically |
```bash
# example using options
npx @docusaurus/migrate migrate --mdx --page ./v1-website ./v2-website
```
:::danger
The migration of pages and MDX is still a work in progress.
We recommend you to try to run the pages without these options, commit, and then try to run the migration again with the `--page` and `--mdx` options.
This way, you'd be able to easily inspect and fix the diff.
:::

View file

@ -0,0 +1,634 @@
---
slug: /migration/manual
toc_max_heading_level: 4
---
# Manual migration
This manual migration process should be run after the [automated migration process](./migration-automated.mdx), to complete the missing parts, or debug issues in the migration CLI output.
## Project setup {#project-setup}
### `package.json` {#packagejson}
#### Scoped package names {#scoped-package-names}
In Docusaurus 2, we use scoped package names:
- `docusaurus` → `@docusaurus/core`
This provides a clear distinction between Docusaurus' official packages and community maintained packages. In another words, all Docusaurus' official packages are namespaced under `@docusaurus/`.
Meanwhile, the default doc site functionalities provided by Docusaurus 1 are now provided by `@docusaurus/preset-classic`. Therefore, we need to add this dependency as well:
```diff title="package.json"
{
dependencies: {
- "docusaurus": "^1.x.x",
+ "@docusaurus/core": "^2.0.0-beta.0",
+ "@docusaurus/preset-classic": "^2.0.0-beta.0",
}
}
```
:::tip
Please use the most recent Docusaurus 2 version, which you can check out [here](https://www.npmjs.com/package/@docusaurus/core) (using the `latest` tag).
:::
#### CLI commands {#cli-commands}
Meanwhile, CLI commands are renamed to `docusaurus <command>` (instead of `docusaurus-command`).
The `"scripts"` section of your `package.json` should be updated as follows:
```json {3-6} title="package.json"
{
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy"
// ...
}
}
```
A typical Docusaurus 2 `package.json` may look like this:
```json title="package.json"
{
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"serve": "docusaurus serve",
"clear": "docusaurus clear"
},
"dependencies": {
"@docusaurus/core": "^2.0.0-beta.0",
"@docusaurus/preset-classic": "^2.0.0-beta.0",
"clsx": "^1.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"browserslist": {
"production": [">0.5%", "not dead", "not op_mini all"],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
```
### Update references to the `build` directory {#update-references-to-the-build-directory}
In Docusaurus 1, all the build artifacts are located within `website/build/<PROJECT_NAME>`.
In Docusaurus 2, it is now moved to just `website/build`. Make sure that you update your deployment configuration to read the generated files from the correct `build` directory.
If you are deploying to GitHub pages, make sure to run `yarn deploy` instead of `yarn publish-gh-pages` script.
### `.gitignore` {#gitignore}
The `.gitignore` in your `website` should contain:
```bash title=".gitignore"
# dependencies
/node_modules
# production
/build
# generated files
.docusaurus
.cache-loader
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
```
### `README` {#readme}
The D1 website may have an existing README file. You can modify it to reflect the D2 changes, or copy the default [Docusaurus v2 README](https://github.com/facebook/docusaurus/blob/main/packages/create-docusaurus/templates/shared/README.md).
## Site configurations {#site-configurations}
### `docusaurus.config.js` {#docusaurusconfigjs}
Rename `siteConfig.js` to `docusaurus.config.js`.
In Docusaurus 2, we split each functionality (blog, docs, pages) into plugins for modularity. Presets are bundles of plugins and for backward compatibility we built a `@docusaurus/preset-classic` preset which bundles most of the essential plugins present in Docusaurus 1.
Add the following preset configuration to your `docusaurus.config.js`.
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// Docs folder path relative to website dir.
path: '../docs',
// Sidebars file relative to website dir.
sidebarPath: require.resolve('./sidebars.json'),
},
// ...
},
],
],
};
```
We recommend moving the `docs` folder into the `website` folder and that is also the default directory structure in v2. [Vercel](https://vercel.com) supports [Docusaurus project deployments out-of-the-box](https://github.com/vercel/vercel/tree/main/examples/docusaurus) if the `docs` directory is within the `website`. It is also generally better for the docs to be within the website so that the docs and the rest of the website code are co-located within one `website` directory.
If you are migrating your Docusaurus v1 website, and there are pending documentation pull requests, you can temporarily keep the `/docs` folder to its original place, to avoid producing conflicts.
Refer to migration guide below for each field in `siteConfig.js`.
### Updated fields {#updated-fields}
#### `baseUrl`, `tagline`, `title`, `url`, `favicon`, `organizationName`, `projectName`, `githubHost`, `scripts`, `stylesheets` {#baseurl-tagline-title-url-favicon-organizationname-projectname-githubhost-scripts-stylesheets}
No actions needed, these configuration fields were not modified.
#### `colors` {#colors}
Deprecated. We wrote a custom CSS framework for Docusaurus 2 called [Infima](https://infima.dev/) which uses CSS variables for theming. The docs are not quite ready yet and we will update here when it is. To overwrite Infima's CSS variables, create your own CSS file (e.g. `./src/css/custom.css`) and import it globally by passing it as an option to `@docusaurus/preset-classic`:
```js {7-9} title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
theme: {
customCss: [require.resolve('./src/css/custom.css')],
},
},
],
],
};
```
Infima uses 7 shades of each color.
```css title="/src/css/custom.css"
/**
* You can override the default Infima variables here.
* Note: this is not a complete list of --ifm- variables.
*/
:root {
--ifm-color-primary: #25c2a0;
--ifm-color-primary-dark: rgb(33, 175, 144);
--ifm-color-primary-darker: rgb(31, 165, 136);
--ifm-color-primary-darkest: rgb(26, 136, 112);
--ifm-color-primary-light: rgb(70, 203, 174);
--ifm-color-primary-lighter: rgb(102, 212, 189);
--ifm-color-primary-lightest: rgb(146, 224, 208);
}
```
We recommend using [ColorBox](https://www.colorbox.io/) to find the different shades of colors for your chosen primary color.
Alteratively, use the following tool to generate the different shades for your website and copy the variables into `src/css/custom.css`.
import ColorGenerator from '@site/src/components/ColorGenerator';
<ColorGenerator />
#### `footerIcon`, `copyright`, `ogImage`, `twitterImage`, `docsSideNavCollapsible` {#footericon-copyright-ogimage-twitterimage-docssidenavcollapsible}
Site meta info such as assets, SEO, copyright info are now handled by themes. To customize them, use the `themeConfig` field in your `docusaurus.config.js`:
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
footer: {
logo: {
alt: 'Meta Open Source Logo',
src: '/img/meta_oss_logo.png',
href: 'https://opensource.facebook.com/',
},
copyright: `Copyright © ${new Date().getFullYear()} Facebook, Inc.`, // You can also put own HTML here.
},
image: 'img/docusaurus.png',
// ...
},
};
```
#### `headerIcon`, `headerLinks` {#headericon-headerlinks}
In Docusaurus 1, header icon and header links were root fields in `siteConfig`:
```js title="siteConfig.js"
headerIcon: 'img/docusaurus.svg',
headerLinks: [
{ doc: "doc1", label: "Getting Started" },
{ page: "help", label: "Help" },
{ href: "https://github.com/", label: "GitHub" },
{ blog: true, label: "Blog" },
],
```
Now, these two fields are both handled by the theme:
```js {6-19} title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
navbar: {
title: 'Docusaurus',
logo: {
alt: 'Docusaurus Logo',
src: 'img/docusaurus.svg',
},
items: [
{to: 'docs/doc1', label: 'Getting Started', position: 'left'},
{to: 'help', label: 'Help', position: 'left'},
{
href: 'https://github.com/',
label: 'GitHub',
position: 'right',
},
{to: 'blog', label: 'Blog', position: 'left'},
],
},
// ...
},
};
```
#### `algolia` {#algolia}
```js {4-8} title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
algolia: {
apiKey: '47ecd3b21be71c5822571b9f59e52544',
indexName: 'docusaurus-2',
algoliaOptions: { //... },
},
// ...
},
};
```
:::caution
Your Algolia DocSearch v1 config (found [here](https://github.com/algolia/docsearch-configs/blob/master/configs)) should be updated for Docusaurus v2 ([example](https://github.com/algolia/docsearch-configs/tree/master/configs/docusaurus-2.json)).
You can contact the DocSearch team (@shortcuts, @s-pace) for support. They can update it for you and trigger a recrawl of your site to restore the search (otherwise you will have to wait up to 24h for the next scheduled crawl)
:::
#### `blogSidebarCount` {#blogsidebarcount}
Deprecated. Pass it as a blog option to `@docusaurus/preset-classic` instead:
```js {8} title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
blog: {
postsPerPage: 10,
},
// ...
},
],
],
};
```
#### `cname` {#cname}
Deprecated. Create a `CNAME` file in your `static` folder instead with your custom domain. Files in the `static` folder will be copied into the root of the `build` folder during execution of the build command.
#### `customDocsPath`, `docsUrl`, `editUrl`, `enableUpdateBy`, `enableUpdateTime` {#customdocspath-docsurl-editurl-enableupdateby-enableupdatetime}
**BREAKING**: `editUrl` should point to (website) Docusaurus project instead of `docs` directory.
Deprecated. Pass it as an option to `@docusaurus/preset-classic` docs instead:
```js {8-20} title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
docs: {
// Equivalent to `customDocsPath`.
path: 'docs',
// Equivalent to `editUrl` but should point to `website` dir instead of `website/docs`.
editUrl: 'https://github.com/facebook/docusaurus/edit/main/website',
// Equivalent to `docsUrl`.
routeBasePath: 'docs',
// Remark and Rehype plugins passed to MDX. Replaces `markdownOptions` and `markdownPlugins`.
remarkPlugins: [],
rehypePlugins: [],
// Equivalent to `enableUpdateBy`.
showLastUpdateAuthor: true,
// Equivalent to `enableUpdateTime`.
showLastUpdateTime: true,
},
// ...
},
],
],
};
```
#### `gaTrackingId` {#gatrackingid}
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
// ...
// highlight-start
googleAnalytics: {
trackingID: 'UA-141789564-1',
},
// highlight-end
},
],
],
};
```
#### `gaGtag` {#gagtag}
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
// ...
// highlight-start
gtag: {
trackingID: 'UA-141789564-1',
},
// highlight-end
},
],
],
};
```
### Removed fields {#removed-fields}
The following fields are all deprecated, you may remove from your configuration file.
- `blogSidebarTitle`
- `cleanUrl` - Clean URL is used by default now.
- `defaultVersionShown` - Versioning is not ported yet. You'd be unable to migration to Docusaurus 2 if you are using versioning. Stay tuned.
- `disableHeaderTitle`
- `disableTitleTagline`
- `docsSideNavCollapsible` is available at `docsPluginOptions.sidebarCollapsible`, and this is turned on by default now.
- `facebookAppId`
- `facebookComments`
- `facebookPixelId`
- `fonts`
- `highlight` - We now use [Prism](https://prismjs.com/) instead of [highlight.js](https://highlightjs.org/).
- `markdownOptions` - We use MDX in v2 instead of Remarkable. Your Markdown options have to be converted to Remark/Rehype plugins.
- `markdownPlugins` - We use MDX in v2 instead of Remarkable. Your Markdown plugins have to be converted to Remark/Rehype plugins.
- `manifest`
- `onPageNav` - This is turned on by default now.
- `separateCss` - It can imported in the same manner as `custom.css` mentioned above.
- `scrollToTop`
- `scrollToTopOptions`
- `translationRecruitingLink`
- `twitter`
- `twitterUsername`
- `useEnglishUrl`
- `users`
- `usePrism` - We now use [Prism](https://prismjs.com/) instead of [highlight.js](https://highlightjs.org/)
- `wrapPagesHTML`
We intend to implement many of the deprecated config fields as plugins in future. Help will be appreciated!
## Urls {#urls}
In v1, all pages were available with or without the `.html` extension.
For example, these 2 pages exist:
- [https://v1.docusaurus.io/docs/en/installation](https://v1.docusaurus.io/docs/en/installation)
- [https://v1.docusaurus.io/docs/en/installation.html](https://v1.docusaurus.io/docs/en/installation.html)
If [`cleanUrl`](https://v1.docusaurus.io/docs/en/site-config#cleanurl-boolean) was:
- `true`: links would target `/installation`
- `false`: links would target `/installation.html`
In v2, by default, the canonical page is `/installation`, and not `/installation.html`.
If you had `cleanUrl: false` in v1, it's possible that people published links to `/installation.html`.
For SEO reasons, and avoiding breaking links, you should configure server-side redirect rules on your hosting provider.
As an escape hatch, you could use [@docusaurus/plugin-client-redirects](../api/plugins/plugin-client-redirects.mdx) to create client-side redirects from `/installation.html` to `/installation`.
```js
module.exports = {
plugins: [
[
'@docusaurus/plugin-client-redirects',
{
fromExtensions: ['html'],
},
],
],
};
```
If you want to keep the `.html` extension as the canonical URL of a page, docs can declare a `slug: installation.html` front matter.
## Components {#components}
### Sidebar {#sidebar}
In previous version, nested sidebar category is not allowed and sidebar category can only contain doc ID. However, v2 allows infinite nested sidebar and we have many types of [Sidebar Item](../guides/docs/sidebar/items.mdx) other than document.
You'll have to migrate your sidebar if it contains category type. Rename `subcategory` to `category` and `ids` to `items`.
```diff title="sidebars.json"
{
- type: 'subcategory',
+ type: 'category',
label: 'My Example Subcategory',
+ items: ['doc1'],
- ids: ['doc1']
},
```
### Footer {#footer}
`website/core/Footer.js` is no longer needed. If you want to modify the default footer provided by Docusaurus, [swizzle](../swizzling.mdx) it:
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic Footer
```
This will copy the current `<Footer />` component used by the theme to a `src/theme/Footer` directory under the root of your site, you may then edit this component for customization.
Do not swizzle the Footer just to add the logo on the left. The logo is intentionally removed in v2 and moved to the bottom. Just configure the footer in `docusaurus.config.js` with `themeConfig.footer`:
```js
module.exports = {
themeConfig: {
footer: {
logo: {
alt: 'Meta Open Source Logo',
src: '/img/meta_oss_logo.png',
href: 'https://opensource.facebook.com',
},
},
},
};
```
### Pages {#pages}
Please refer to [creating pages](guides/creating-pages.mdx) to learn how Docusaurus 2 pages work. After reading that, notice that you have to move `pages/en` files in v1 to `src/pages` instead.
In Docusaurus v1, pages received the `siteConfig` object as props.
In Docusaurus v2, get the `siteConfig` object from `useDocusaurusContext` instead.
In v2, you have to apply the theme layout around each page. The Layout component takes metadata props.
`CompLibrary` is deprecated in v2, so you have to write your own React component or use Infima styles (Docs will be available soon, sorry about that! In the meanwhile, inspect the V2 website or view https://infima.dev/ to see what styles are available).
You can migrate CommonJS to ES6 imports/exports.
Here's a typical Docusaurus v2 page:
```jsx
import React from 'react';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
const MyPage = () => {
const {siteConfig} = useDocusaurusContext();
return (
<Layout title={siteConfig.title} description={siteConfig.tagline}>
<div className="hero text--center">
<div className="container ">
<div className="padding-vert--md">
<h1 className="hero__title">{siteConfig.title}</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
</div>
<div>
<Link
to="/docs/get-started"
className="button button--lg button--outline button--primary">
Get started
</Link>
</div>
</div>
</div>
</Layout>
);
};
export default MyPage;
```
The following code could be helpful for migration of various pages:
- Index page - [Flux](https://github.com/facebook/flux/blob/master/website/src/pages/index.js/) (recommended), [Docusaurus 2](https://github.com/facebook/docusaurus/blob/main/website/src/pages/index.js/), [Hermes](https://github.com/facebook/hermes/blob/main/website/src/pages/index.js/)
- Help/Support page - [Docusaurus 2](https://github.com/facebook/docusaurus/blob/main/website/src/pages/help.js/), [Flux](http://facebook.github.io/flux/support)
## Content {#content}
### Replace AUTOGENERATED_TABLE_OF_CONTENTS {#replace-autogenerated_table_of_contents}
This feature is replaced by [inline table of content](../guides/markdown-features/markdown-features-toc.mdx#inline-table-of-contents)
### Update Markdown syntax to be MDX-compatible {#update-markdown-syntax-to-be-mdx-compatible}
In Docusaurus 2, the Markdown syntax has been changed to [MDX](https://mdxjs.com/). Hence there might be some broken syntax in the existing docs which you would have to update. A common example is self-closing tags like `<img>` and `<br>` which are valid in HTML would have to be explicitly closed now ( `<img/>` and `<br/>`). All tags in MDX documents have to be valid JSX.
Front matter is parsed by [gray-matter](https://github.com/jonschlinkert/gray-matter). If your front matter use special characters like `:`, you now need to quote it: `title: Part 1: my part1 title` → `title: "Part 1: my part1 title"`.
**Tips**: You might want to use some online tools like [HTML to JSX](https://transform.tools/html-to-jsx) to make the migration easier.
### Language-specific code tabs {#language-specific-code-tabs}
Refer to the [multi-language support code blocks](../guides/markdown-features/markdown-features-code-blocks.mdx#multi-language-support-code-blocks) section.
### Front matter {#front-matter}
The Docusaurus front matter fields for the blog have been changed from camelCase to snake_case to be consistent with the docs.
The fields `authorFBID` and `authorTwitter` have been deprecated. They are only used for generating the profile image of the author which can be done via the `authors` field.
## Deployment {#deployment}
The `CNAME` file used by GitHub Pages is not generated anymore, so be sure you have created it in `/static/CNAME` if you use a custom domain.
The blog RSS feed is now hosted at `/blog/rss.xml` instead of `/blog/feed.xml`. You may want to configure server-side redirects so that users' subscriptions keep working.
## Test your site {#test-your-site}
After migration, your folder structure should look like this:
```bash
my-project
├── docs
└── website
├── blog
├── src
│ ├── css
│ │ └── custom.css
│ └── pages
│ └── index.js
├── package.json
├── sidebars.json
├── .gitignore
├── docusaurus.config.js
└── static
```
Start the development server and fix any errors:
```bash npm2yarn
cd website
npm start
```
You can also try to build the site for production:
```bash npm2yarn
npm run build
```

View file

@ -0,0 +1,105 @@
---
slug: /migration
---
# Migration overview
This doc guides you through migrating an existing Docusaurus 1 site to Docusaurus 2.
We try to make this as easy as possible, and provide a migration CLI.
## Main differences {#main-differences}
Docusaurus 1 is a pure documentation site generator, using React as a server-side template engine, but not loading React on the browser.
Docusaurus 2, rebuilt from the ground up, generates a single-page-application, using the full power of React in the browser. It allows for more customizability but preserved the best parts of Docusaurus 1 - easy to get started, versioned docs, and i18n.
Beyond that, Docusaurus 2 is a **performant static site generator** and can be used to create common content-driven websites (e.g. Documentation, Blogs, Product Landing and Marketing Pages, etc) extremely quickly.
While our main focus will still be helping you get your documentations right and well, it is possible to build any kind of website using Docusaurus 2 as it is just a React application. **Docusaurus can now be used to build any website, not just documentation websites.**
## Docusaurus 1 structure {#docusaurus-1-structure}
Your Docusaurus 1 site should have the following structure:
```bash
├── docs
└── website
├── blog
├── core
│ └── Footer.js
├── package.json
├── pages
├── sidebars.json
├── siteConfig.js
└── static
```
## Docusaurus 2 structure {#docusaurus-2-structure}
After the migration, your Docusaurus 2 site could look like:
```sh
├── docs
└── website
├── blog
├── src
│ ├── components
│ ├── css
│ └── pages
├── static
├── package.json
├── sidebars.json
├── docusaurus.config.js
```
:::info
This migration does not change the `/docs` folder location, but Docusaurus v2 sites generally have the `/docs` folder inside `/website`
You are free to put the `/docs` folder anywhere you want after having migrated to v2.
:::
## Migration process {#migration-process}
There are multiple things to migrate to obtain a fully functional Docusaurus 2 website:
- packages
- CLI commands
- site configuration
- Markdown files
- sidebars file
- pages, components and CSS
- versioned docs
- i18n support 🚧
## Automated migration process {#automated-migration-process}
The [migration CLI](./migration-automated.mdx) will handle many things of the migration for you.
However, some parts can't easily be automated, and you will have to fallback to the manual process.
:::note
We recommend running the migration CLI, and complete the missing parts thanks to the manual migration process.
:::
## Manual migration process {#manual-migration-process}
Some parts of the migration can't be automated (particularly the pages), and you will have to migrate them manually.
The [manual migration guide](./migration-manual.mdx) will give you all the manual steps.
## Support {#support}
For any questions, you can ask in the [`#migration-v1-to-v2` Discord channel](https://discord.gg/C3P6CxMMxY).
Feel free to tag [@slorber](https://github.com/slorber) in any migration PRs if you would like us to have a look.
We also have volunteers willing to [help you migrate your v1 site](https://github.com/facebook/docusaurus/issues/1834).
## Example migration PRs {#example-migration-prs}
You might want to refer to our migration PRs for [Create React App](https://github.com/facebook/create-react-app/pull/7785) and [Flux](https://github.com/facebook/flux/pull/471) as examples of how a migration for a basic Docusaurus v1 site can be done.

View file

@ -0,0 +1,167 @@
---
slug: /migration/translated-sites
---
# Translated sites
This page explains how migrate a translated Docusaurus v1 site to Docusaurus v2.
## i18n differences {#i18n-differences}
Docusaurus v2 i18n is conceptually quite similar to Docusaurus v1 i18n with a few differences.
It is not tightly coupled to Crowdin, and you can use Git or another SaaS instead.
### Different filesystem paths {#different-filesystem-paths}
On Docusaurus v2, localized content is generally found at `website/i18n/[locale]`.
Docusaurus v2 is modular based on a plugin system, and each plugin is responsible to manage its own translations.
Each plugin has its own i18n subfolder, like: `website/i18n/fr/docusaurus-plugin-content-blog`
### Updated translation APIs {#updated-translation-apis}
With Docusaurus v1, you translate your pages with `<translate>`:
```jsx
const translate = require('../../server/translate.js').translate;
<h2>
<translate desc="the header description">
This header will be translated
</translate>
</h2>;
```
On Docusaurus v2, you translate your pages with `<Translate>`
```jsx
import Translate from '@docusaurus/Translate';
<h2>
<Translate id="header.translation.id" description="the header description">
This header will be translated
</Translate>
</h2>;
```
:::note
The `write-translations` CLI still works to extract translations from your code.
The code translations are now added to `i18n/[locale]/code.json` using Chrome i18n JSON format.
:::
### Stricter Markdown parser {#stricter-markdown-parser}
Docusaurus v2 is using [MDX](https://mdxjs.com/) to parse Markdown files.
MDX compiles Markdown files to React components, is stricter than the Docusaurus v1 parser, and will make your build fail on error instead of rendering some bad content.
Also, the HTML elements must be replaced by JSX elements.
This is particularly important for i18n because if your translations are not good on Crowdin and use invalid Markup, your v2 translated site might fail to build: you may need to do some translation cleanup to fix the errors.
## Migration strategies {#migration-strategies}
This section will help you figure out how to **keep your existing v1 translations after you migrate to v2**.
There are **multiple possible strategies** to migrate a Docusaurus v1 site using Crowdin, with different tradeoffs.
:::caution
This documentation is a best-effort to help you migrate, please help us improve it if you find a better way!
:::
Before all, we recommend to:
- Migrate your v1 Docusaurus site to v2 without the translations
- Get familiar with the [new i18n system of Docusaurus v2](../i18n/i18n-introduction.mdx) an
- Make Crowdin work for your v2 site, using a new and untranslated Crowdin project and the [Crowdin tutorial](../i18n/i18n-crowdin.mdx)
:::danger
Don't try to migrate without understanding both Crowdin and Docusaurus v2 i18n.
:::
### Create a new Crowdin project {#create-a-new-crowdin-project}
To avoid any **risk of breaking your v1 site in production**, one possible strategy is to duplicate the original v1 Crowdin project.
:::info
This strategy was used to [upgrade the Jest website](https://jestjs.io/blog/2021/03/09/jest-website-upgrade).
:::
Unfortunately, Crowdin does not have any "Duplicate/clone Project" feature, which makes things complicated.
- Download the translation memory of your original project in `.tmx` format (`https://crowdin.com/project/<ORIGINAL_PROJECT>/settings#tm` > `View Records`)
- Upload the translation memory to your new project (`https://crowdin.com/project/<NEW_PROJECT>/settings#tm` > `View Records`)
- Reconfigure `crowdin.yml` for Docusaurus v2 according to the i18n docs
- Upload the Docusaurus v2 source files with the Crowdin CLI to the new project
- Mark sensitive strings like `id` or `slug` as "hidden string" on Crowdin
- On the "Translations" tab, click on "Pre-Translation > via TM" (`https://crowdin.com/project/<NEW_PROJECT>/settings#translations`)
- Try first with "100% match" (more content will be translated than "Perfect"), and pre-translate your sources
- Download the Crowdin translations locally
- Try to run/build your site and see if there are any errors
You will likely have errors on your first-try: the pre-translation might try to translate things that it should not be translated (front matter, admonition, code blocks...), and the translated MD files might be invalid for the MDX parser.
You will have to fix all the errors until your site builds. You can do that by modifying the translated MD files locally, and fix your site for one locale at a time using `docusaurus build --locale fr`.
There is no ultimate guide we could write to fix these errors, but common errors are due to:
- Not marking enough strings as "hidden strings" in Crowdin, leading to pre-translation trying to translate these strings.
- Having bad v1 translations, leading to invalid markup in v2: bad HTML elements inside translations and unclosed tags
- Anything rejected by the MDX parser, like using HTML elements instead of JSX elements (use the [MDX playground](https://mdxjs.com/playground/) for debugging)
You might want to repeat this pre-translation process, eventually trying the "Perfect" option and limiting pre-translation only some languages/files.
:::tip
Use [`mdx-code-block`](../i18n/i18n-crowdin.mdx#mdx-solutions) around problematic Markdown elements: Crowdin is less likely mess things up with code blocks.
:::
:::note
You will likely notice that some things were translated on your old project, but are now untranslated in your new project.
The Crowdin Markdown parser is evolving other time and each Crowdin project has a different parser version, which can lead to pre-translation not being able to pre-translate all the strings.
This parser version is undocumented, and you will have to ask the Crowdin support to know your project's parser version and fix one specific version.
Using the same CLI version and parser version across the 2 Crowdin projects might give better results.
:::
:::danger
Crowdin has an "upload translations" feature, but in our experience it does not give very good results for Markdown
:::
### Use the existing Crowdin project {#use-the-existing-crowdin-project}
If you don't mind modifying your existing Crowdin project and risking to mess things up, it may be possible to use the Crowdin branch system.
:::caution
This workflow has not been tested in practice, please report us how good it is.
:::
This way, you wouldn't need to create a new Crowdin project, transfer the translation memory, apply pre-translations, and try to fix the pre-translations errors.
You could create a Crowdin branch for Docusaurus v2, where you upload the v2 sources, and merge the Crowdin branch to main once ready.
### Use Git instead of Crowdin {#use-git-instead-of-crowdin}
It is possible to migrate away of Crowdin, and add the translation files to Git instead.
Use the Crowdin CLI to download the v1 translated files, and put these translated files at the correct Docusaurus v2 filesystem location.

View file

@ -0,0 +1,176 @@
---
slug: /migration/versioned-sites
---
# Versioned sites
Read up https://docusaurus.io/blog/2018/09/11/Towards-Docusaurus-2#versioning first for problems in v1's approach.
:::note
The versioned docs should normally be migrated correctly by the [migration CLI](./migration-automated.mdx)
:::
## Migrate your `versioned_docs` front matter {#migrate-your-versioned_docs-front-matter}
Unlike v1, The Markdown header for each versioned doc is no longer altered by using `version-${version}-${original_id}` as the value for the actual ID field. See scenario below for better explanation.
For example, if you have a `docs/hello.md`.
```md
---
id: hello
title: Hello, World !
---
Hi, Endilie here :)
```
When you cut a new version 1.0.0, in Docusaurus v1, `website/versioned_docs/version-1.0.0/hello.md` looks like this:
```md
---
id: version-1.0.0-hello
title: Hello, World !
original_id: hello
---
Hi, Endilie here :)
```
In comparison, Docusaurus 2 `website/versioned_docs/version-1.0.0/hello.md` looks like this (exactly same as original)
```md
---
id: hello
title: Hello, World !
---
Hi, Endilie here :)
```
Since we're going for snapshot and allow people to move (and edit) docs easily inside version. The `id` front matter is no longer altered and will remain the same. Internally, it is set as `version-${version}/${id}`.
Essentially, here are the necessary changes in each versioned_docs file:
```diff {2-3,5}
---
- id: version-1.0.0-hello
+ id: hello
title: Hello, World !
- original_id: hello
---
Hi, Endilie here :)
```
## Migrate your `versioned_sidebars` {#migrate-your-versioned_sidebars}
- Refer to `versioned_docs` ID as `version-${version}/${id}` (v2) instead of `version-${version}-${original_id}` (v1).
Because in v1 there is a good chance someone created a new file with front matter ID `"version-${version}-${id}"` that can conflict with `versioned_docs` ID.
For example, Docusaurus 1 can't differentiate `docs/xxx.md`
```md
---
id: version-1.0.0-hello
---
Another content
```
vs `website/versioned_docs/version-1.0.0/hello.md`
```md
---
id: version-1.0.0-hello
title: Hello, World !
original_id: hello
---
Hi, Endilie here :)
```
Since we don't allow `/` in v1 & v2 for front matter, conflicts are less likely to occur.
So v1 users need to migrate their versioned_sidebars file
Example `versioned_sidebars/version-1.0.0-sidebars.json`:
```diff {2-3,5-6,9-10} title="versioned_sidebars/version-1.0.0-sidebars.json"
{
+ "version-1.0.0/docs": {
- "version-1.0.0-docs": {
"Test": [
+ "version-1.0.0/foo/bar",
- "version-1.0.0-foo/bar",
],
"Guides": [
+ "version-1.0.0/hello",
- "version-1.0.0-hello"
]
}
}
```
## Populate your `versioned_sidebars` and `versioned_docs` {#populate-your-versioned_sidebars-and-versioned_docs}
In v2, we use snapshot approach for documentation versioning. **Every versioned docs does not depends on other version**. It is possible to have `foo.md` in `version-1.0.0` but it doesn't exist in `version-1.2.0`. This is not possible in previous version due to Docusaurus v1 fallback functionality (https://v1.docusaurus.io/docs/en/versioning#fallback-functionality).
For example, if your `versions.json` looks like this in v1
```json title="versions.json"
["1.1.0", "1.0.0"]
```
Docusaurus v1 creates versioned docs **if and only if the doc content is different**. Your docs structure might look like this if the only doc changed from v1.0.0 to v1.1.0 is `hello.md`.
```bash
website
├── versioned_docs
│ ├── version-1.1.0
│ │ └── hello.md
│ └── version-1.0.0
│ ├── foo
│ │ └── bar.md
│ └── hello.md
├── versioned_sidebars
│ └── version-1.0.0-sidebars.json
```
In v2, you have to populate the missing `versioned_docs` and `versioned_sidebars` (with the right front matter and ID reference too).
```bash {3-5,12}
website
├── versioned_docs
│ ├── version-1.1.0
│ │ ├── foo
│ │ │ └── bar.md
│ │ └── hello.md
│ └── version-1.0.0
│ ├── foo
│ │ └── bar.md
│ └── hello.md
├── versioned_sidebars
│ ├── version-1.1.0-sidebars.json
│ └── version-1.0.0-sidebars.json
```
## Convert style attributes to style objects in MDX {#convert-style-attributes-to-style-objects-in-mdx}
Docusaurus 2 uses JSX for doc files. If you have any style attributes in your Docusaurus 1 docs, convert them to style objects, like this:
```diff
---
id: demo
title: Demo
---
## Section
hello world
- pre style="background: black">zzz</pre>
+ pre style={{background: 'black'}}>zzz</pre>
```

View file

@ -0,0 +1,24 @@
# Playground
Playgrounds allow you to run Docusaurus **in your browser, without installing anything**!
They are mostly useful for:
- Testing Docusaurus
- Reporting bugs
Use [docusaurus.new](https://docusaurus.new) as an easy-to-remember shortcut.
Choose one of the available options below.
```mdx-code-block
import {PlaygroundCardsRow} from '@site/src/components/Playground';
<PlaygroundCardsRow />
```
:::tip
For convenience, we'll remember your choice next time you visit [docusaurus.new](https://docusaurus.new).
:::

View file

@ -0,0 +1,315 @@
---
keywords:
- algolia
- search
---
# Search
There are a few options you can use to add search to your website:
- 🥇 [Algolia DocSearch](#using-algolia-docsearch) (**official**)
- 👥 [Typesense DocSearch](#using-typesense-docsearch)
- 👥 [Local Search](#using-local-search)
- 👥 [Your own `SearchBar` component](#using-your-own-search)
:::info
🥇 Docusaurus provides **first-class support** for [Algolia DocSearch](#using-algolia-docsearch).
👥 Other options are **maintained by the community**: please report bugs to their respective repositories.
:::
## 🥇 Using Algolia DocSearch {#using-algolia-docsearch}
Docusaurus has **official support** for [Algolia DocSearch](https://docsearch.algolia.com).
The service is **free** for any open-source project: just make sure to read the [checklist](https://docsearch.algolia.com/docs/who-can-apply/) and [apply to the DocSearch program](https://docsearch.algolia.com/apply).
DocSearch crawls your website once a week (the schedule is configurable from the web interface) and aggregates all the content in an Algolia index. This content is then queried directly from your front-end using the Algolia API.
If your website is [not eligible](https://docsearch.algolia.com/docs/who-can-apply) for the free, hosted version of DocSearch, or if your website sits behind a firewall and is not public, then you can [run your own](https://docsearch.algolia.com/docs/run-your-own/) DocSearch crawler.
:::note
By default, the Docusaurus preset generates a [sitemap.xml](https://docusaurus.io/sitemap.xml) that the Algolia crawler can use.
:::
:::info From the old docsearch?
You can read more about migration from the legacy DocSearch infra in [our blog post](/blog/2021/11/21/algolia-docsearch-migration) or [the DocSearch migration docs](https://docsearch.algolia.com/docs/migrating-from-legacy).
:::
### Index Configuration {#algolia-index-configuration}
After your application has been approved and deployed, you will receive an email with all the details for you to add DocSearch to your project. Editing and managing your crawls can be done via [the web interface](https://crawler.algolia.com/). Indices are readily available after deployment, so manual configuration usually isn't necessary.
:::tip
It is highly recommended to use a config similar to the [**Docusaurus 2 website config**](https://docsearch.algolia.com/docs/templates/#docusaurus-v2-template).
:::
### Connecting Algolia {#connecting-algolia}
Docusaurus' own `@docusaurus/preset-classic` supports Algolia DocSearch integration. If you use the classic preset, no additional installation is needed.
<details>
<summary>Installation steps when not using <code>@docusaurus/preset-classic</code></summary>
1. Install the package:
```bash npm2yarn
npm install --save @docusaurus/theme-search-algolia
```
2. Register the theme in `docusaurus.config.js`:
```js title="docusaurus.config.js"
module.exports = {
title: 'My site',
// ...
themes: ['@docusaurus/theme-search-algolia'],
themeConfig: {
// ...
},
};
```
</details>
Then, add an `algolia` field in your `themeConfig`. **[Apply for DocSearch](https://docsearch.algolia.com/apply/)** to get your Algolia index and API key.
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
// ...
// highlight-start
algolia: {
// The application ID provided by Algolia
appId: 'YOUR_APP_ID',
// Public API key: it is safe to commit it
apiKey: 'YOUR_SEARCH_API_KEY',
indexName: 'YOUR_INDEX_NAME',
// Optional: see doc section below
contextualSearch: true,
// Optional: Specify domains where the navigation should occur through window.location instead on history.push. Useful when our Algolia config crawls multiple documentation sites and we want to navigate with window.location.href to them.
externalUrlRegex: 'external\\.com|domain\\.com',
// Optional: Replace parts of the item URLs from Algolia. Useful when using the same search index for multiple deployments using a different baseUrl. You can use regexp or string in the `from` param. For example: localhost:3000 vs myCompany.com/docs
replaceSearchResultPathname: {
from: '/docs/', // or as RegExp: /\/docs\//
to: '/',
},
// Optional: Algolia search parameters
searchParameters: {},
// Optional: path for search page that enabled by default (`false` to disable it)
searchPagePath: 'search',
//... other Algolia params
},
// highlight-end
},
};
```
:::info
The `searchParameters` option used to be named `algoliaOptions` in Docusaurus v1.
Refer to its [official DocSearch documentation](https://docsearch.algolia.com/docs/api#searchparameters) for possible values.
:::
:::caution
The search feature will not work reliably until Algolia crawls your site.
If search doesn't work after any significant change, please use the Algolia dashboard to **trigger a new crawl**.
:::
### Contextual search {#contextual-search}
Contextual search is **enabled by default**.
It ensures that search results are **relevant to the current language and version**.
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
// ...
// highlight-start
algolia: {
contextualSearch: true,
},
// highlight-end
},
};
```
Let's consider you have 2 docs versions (**v1** and **v2**) and 2 languages (`en` and `fr`).
When browsing v2 docs, it would be odd to return search results for the v1 documentation. Sometimes v1 and v2 docs are quite similar, and you would end up with duplicate search results for the same query (one result per version).
Similarly, when browsing the French site, it would be odd to return search results for the English docs.
To solve this problem, the contextual search feature understands that you are browsing a specific docs version and language, and will create the search query filters dynamically.
- on `/en/docs/v1/myDoc`, search results will only include **English** results for the **v1** docs (+ other unversioned pages)
- on `/fr/docs/v2/myDoc`, search results will only include **French** results for the **v2** docs (+ other unversioned pages)
:::info
When using `contextualSearch: true` (default), the contextual facet filters will be merged with the ones provided with `algolia.searchParameters.facetFilters` .
For specific needs, you can disable `contextualSearch` and define your own `facetFilters`:
```js title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
// ...
// highlight-start
algolia: {
contextualSearch: false,
searchParameters: {
facetFilters: ['language:en', ['filter1', 'filter2'], 'filter3'],
},
},
// highlight-end
},
};
```
Refer to the relevant [Algolia faceting documentation](https://www.algolia.com/doc/guides/managing-results/refine-results/faceting/).
:::
### Styling your Algolia search {#styling-your-algolia-search}
By default, DocSearch comes with a fine-tuned theme that was designed for accessibility, making sure that colors and contrasts respect standards.
Still, you can reuse the [Infima CSS variables](styling-layout.mdx#styling-your-site-with-infima) from Docusaurus to style DocSearch by editing the `/src/css/custom.css` file.
```css title="/src/css/custom.css"
[data-theme='light'] .DocSearch {
/* --docsearch-primary-color: var(--ifm-color-primary); */
/* --docsearch-text-color: var(--ifm-font-color-base); */
--docsearch-muted-color: var(--ifm-color-secondary-darkest);
--docsearch-container-background: rgba(94, 100, 112, 0.7);
/* Modal */
--docsearch-modal-background: var(--ifm-color-secondary-lighter);
/* Search box */
--docsearch-searchbox-background: var(--ifm-color-secondary);
--docsearch-searchbox-focus-background: var(--ifm-color-white);
/* Hit */
--docsearch-hit-color: var(--ifm-font-color-base);
--docsearch-hit-active-color: var(--ifm-color-white);
--docsearch-hit-background: var(--ifm-color-white);
/* Footer */
--docsearch-footer-background: var(--ifm-color-white);
}
[data-theme='dark'] .DocSearch {
--docsearch-text-color: var(--ifm-font-color-base);
--docsearch-muted-color: var(--ifm-color-secondary-darkest);
--docsearch-container-background: rgba(47, 55, 69, 0.7);
/* Modal */
--docsearch-modal-background: var(--ifm-background-color);
/* Search box */
--docsearch-searchbox-background: var(--ifm-background-color);
--docsearch-searchbox-focus-background: var(--ifm-color-black);
/* Hit */
--docsearch-hit-color: var(--ifm-font-color-base);
--docsearch-hit-active-color: var(--ifm-color-white);
--docsearch-hit-background: var(--ifm-color-emphasis-100);
/* Footer */
--docsearch-footer-background: var(--ifm-background-surface-color);
--docsearch-key-gradient: linear-gradient(
-26.5deg,
var(--ifm-color-emphasis-200) 0%,
var(--ifm-color-emphasis-100) 100%
);
}
```
### Customizing the Algolia search behavior {#customizing-the-algolia-search-behavior}
<!-- TODO: update options link once the documentation is available on the DocSearch website -->
Algolia DocSearch supports a [list of options](https://autocomplete-experimental.netlify.app/docs/DocSearchModal#reference) that you can pass to the `algolia` field in the `docusaurus.config.js` file.
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
// ...
algolia: {
apiKey: 'YOUR_API_KEY',
indexName: 'YOUR_INDEX_NAME',
// Options...
},
},
};
```
### Editing the Algolia search component {#editing-the-algolia-search-component}
If you prefer to edit the Algolia search React component, [swizzle](swizzling.mdx) the `SearchBar` component in `@docusaurus/theme-search-algolia`:
```bash npm2yarn
npm run swizzle @docusaurus/theme-search-algolia SearchBar
```
### Support {#algolia-support}
The Algolia DocSearch team can help you figure out search problems on your site.
You can contact them by [email](mailto:documentationsearch@algolia.com) or on [Discord](https://discord.gg/tXdr5mP).
Docusaurus also has an `#algolia` channel on [Discord](https://discordapp.com/invite/docusaurus).
## 👥 Using Typesense DocSearch {#using-typesense-docsearch}
[Typesense](https://typesense.org) DocSearch works similar to Algolia DocSearch, except that your website is indexed into a Typesense search cluster.
Typesense is an [open source](https://github.com/typesense/typesense) instant-search engine that you can either:
- [Self-Host](https://typesense.org/docs/latest/guide/install-typesense.html#option-2-local-machine-self-hosting) on your own servers or
- Use the Managed [Typesense Cloud](https://cloud.typesense.org) service.
Similar to Algolia DocSearch, there are two components:
- [typesense-docsearch-scraper](https://github.com/typesense/typesense-docsearch-scraper) - which scrapes your website and indexes the data in your Typesense cluster.
- [docusaurus-theme-search-typesense](https://github.com/typesense/docusaurus-theme-search-typesense) - a search bar UI component to add to your website.
Read a step-by-step walk-through of how to [run typesense-docsearch-scraper here](https://typesense.org/docs/latest/guide/docsearch.html#step-1-set-up-docsearch-scraper) and how to [install the Search Bar in your Docusaurus Site here](https://typesense.org/docs/latest/guide/docsearch.html#option-a-docusaurus-powered-sites).
## 👥 Using Local Search {#using-local-search}
You can use a local search plugin for websites where the search index is small and can be downloaded to your users' browsers when they visit your website.
You'll find a list of community-supported [local search plugins listed here](https://docusaurus.io/community/resources#search).
## 👥 Using your own search {#using-your-own-search}
To use your own search, swizzle the `SearchBar` component in `@docusaurus/theme-classic`
```bash npm2yarn
npm run swizzle @docusaurus/theme-classic SearchBar
```
This will create an `src/themes/SearchBar` file in your project folder. Restart your dev server and edit the component, you will see that Docusaurus uses your own `SearchBar` component now.
**Notes**: You can alternatively [swizzle from Algolia SearchBar](#editing-the-algolia-search-component) and create your own search component from there.

View file

@ -0,0 +1,161 @@
---
description: How to make your Docusaurus site maximally search-engine-friendly.
sidebar_label: SEO
keywords:
- seo
- positioning
---
# Search engine optimization (SEO)
import BrowserWindow from '@site/src/components/BrowserWindow';
Docusaurus supports search engine optimization in a variety of ways.
## Global metadata {#global-metadata}
Provide global meta attributes for the entire site through the [site configuration](./configuration.mdx#site-metadata). The metadata will all be rendered in the HTML `<head>` using the key-value pairs as the prop name and value.
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
metadata: [{name: 'keywords', content: 'cooking, blog'}],
// This would become <meta name="keywords" content="cooking, blog"> in the generated HTML
},
};
```
Docusaurus adds some metadata out-of-the-box. For example, if you have configured [i18n](./i18n/i18n-introduction.mdx), you will get a [`hreflang`](https://developers.google.com/search/docs/advanced/crawling/localized-versions) alternate link.
To read more about types of meta tags, visit [the MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta).
## Single page metadata {#single-page-metadata}
Similar to [global metadata](#global-metadata), Docusaurus also allows for the addition of meta-information to individual pages. Follow [this guide](./guides/markdown-features/markdown-features-head-metadata.mdx) for configuring the `<head>` tag. In short:
```md title="my-markdown-page.md"
# A cooking guide
<head>
<meta name="keywords" content="cooking, blog">
</head>
Some content...
```
Docusaurus automatically adds `description`, `title`, canonical URL links, and other useful metadata to each Markdown page. They are configurable through front matter:
```md
---
title: Title for search engines; can be different from the actual heading
description: A short description of this page
image: a thumbnail image to be shown in social media cards
keywords: [keywords, describing, the main topics]
---
```
When creating your React page, adding these fields in `Layout` would also improve SEO.
:::tip
Prefer to use front matter for fields like `description` and `keywords`: Docusaurus will automatically apply this to both `description` and `og:description`, while you would have to manually declare two metadata tags when using the `<head>` tag.
:::
For JSX pages, you can use the Docusaurus [`<Head>`](docusaurus-core.mdx#head) component.
```jsx title="my-react-page.jsx"
import React from 'react';
import Layout from '@theme/Layout';
import Head from '@docusaurus/Head';
export default function page() {
return (
<Layout title="Page" description="A React page demo">
<Head>
<meta property="og:image" content="image.png" />
</Head>
{/* ... */}
</Layout>
);
}
```
:::tip
For convenience, the default theme `<Layout>` component accept `title` and `description` as props.
:::
## Static HTML generation {#static-html-generation}
Docusaurus is a static site generator—HTML files are statically generated for every URL route, which helps search engines discover your content more easily.
## Image meta description {#image-meta-description}
The alt tag for an image tells the search engine what the image is about, and is used when the image can't be visually seen, e.g. when using a screen reader, or when the image is broken. Alt tags are commonly supported in Markdown.
You may also add a title for your image—this doesn't impact SEO much but is displayed as a tooltip when hovering above the image, usually used to provide hints.
```md
![Docusaurus banner](./assets/docusaurus-asset-example-banner.png 'Image title')
```
<BrowserWindow>
![Docusaurus banner](./assets/docusaurus-asset-example-banner.png 'Image title')
</BrowserWindow>
## Rich search information {#rich-search-information}
Docusaurus blogs support [rich search results](https://search.google.com/test/rich-results) out-of-the-box to get maximum search engine experience. The information is created depending on your meta information in blog/global configuration. In order to get the benefits of the rich search information, fill in the information about the post's publish date, authors, and image, etc. Read more about the meta-information [here](./blog.mdx).
## Robots file {#robots-file}
A `robots.txt` file regulates search engines' behavior about which should be displayed and which shouldn't. You can provide it as [static asset](./static-assets.mdx). The following would allow access to all sub-pages from all requests:
```text title="static/robots.txt"
User-agent: *
Disallow:
```
Read more about the robots file in [the Google documentation](https://developers.google.com/search/docs/advanced/robots/intro).
:::caution
**Important**: the `robots.txt` file does **not** prevent HTML pages from being indexed.
To prevent your whole Docusaurus site from being indexed, use the [`noIndex`](./api/docusaurus.config.js.mdx#noIndex) site config. Some [hosting providers](./deployment.mdx) may also let you configure a `X-Robots-Tag: noindex` HTTP header (GitHub Pages does not support this).
To prevent a single page from being indexed, use `<meta name="robots" content="noindex">` as [page metadata](#single-page-metadata). Read more about the [robots meta tag](https://developers.google.com/search/docs/advanced/robots/robots_meta_tag).
:::
## Sitemap file {#sitemap-file}
Docusaurus provides the [`@docusaurus/plugin-sitemap`](./api/plugins/plugin-sitemap.mdx) plugin, which is shipped with `preset-classic` by default. It autogenerates a `sitemap.xml` file which will be available at `https://example.com/[baseUrl]/sitemap.xml` after the production build. This sitemap metadata helps search engine crawlers crawl your site more accurately.
:::tip
The sitemap plugin automatically filters pages containing a `noindex` [robots meta directive](https://developers.google.com/search/docs/advanced/robots/robots_meta_tag).
For example, [`/examples/noIndex`](/examples/noIndex) is not included in the [Docusaurus sitemap.xml file](pathname:///sitemap.xml) because it contains the following [page metadata](#single-page-metadata):
```html
<head>
<meta name="robots" content="noindex, nofollow" />
</head>
```
:::
## Human readable links {#human-readable-links}
Docusaurus uses your file names as links, but you can always change that using slugs, see this [tutorial](./guides/docs/docs-introduction.mdx#document-id) for more details.
## Structured content {#structured-content}
Search engines rely on the HTML markup such as `<h2>`, `<table>`, etc., to understand the structure of your webpage. When Docusaurus renders your pages, semantic markup, e.g. `<aside>`, `<nav>`, `<main>`, are used to divide the different sections of the page, helping the search engine to locate parts like sidebar, navbar, and the main page content.
Most [CommonMark](https://spec.commonmark.org/0.30/#atx-headings) syntaxes have their corresponding HTML tags. By using Markdown consistently in your project, you will make it easier for search engines to understand your page content.

View file

@ -0,0 +1,109 @@
---
description: Static assets are the non-code files that are directly copied to the build output. Learn about how they are handled and what the best practices of using static assets are.
---
# Static Assets
Static assets are the non-code files that are directly copied to the build output. They include images, stylesheets, favicons, fonts, etc.
By default, you are suggested to put these assets in the `static` folder. Every file you put into **that directory will be copied** into the root of the generated `build` folder with the directory hierarchy preserved. E.g. if you add a file named `sun.jpg` to the static folder, it will be copied to `build/sun.jpg`.
This means that:
- for site `baseUrl: '/'`, the image `/static/img/docusaurus.png` will be served at `/img/docusaurus.png`.
- for site `baseUrl: '/subpath/'`, the image `/static/img/docusaurus.png` will be served at `/subpath/img/docusaurus.png`.
You can customize the static directory sources in `docusaurus.config.js`. For example, we can add `public` as another possible path:
```js title="docusaurus.config.js"
module.exports = {
title: 'My site',
staticDirectories: ['public', 'static'],
// ...
};
```
Now, all files in `public` as well as `static` will be copied to the build output.
## Referencing your static asset {#referencing-your-static-asset}
### In JSX {#in-jsx}
In JSX, you can reference assets from the `static` folder in your code using absolute URLs, but this is not ideal because changing the site `baseUrl` will **break those links**. For the image `<img src="/img/docusaurus.png" />` served at `https://example.com/test`, the browser will try to resolve it from the URL root, i.e. as `https://example.com/img/docusaurus.png`, which will fail because it's actually served at `https://example.com/test/img/docusaurus.png`.
You can `import()` or `require()` the static asset (recommended), or use the `useBaseUrl` utility function: both prepend the `baseUrl` to paths for you.
Examples:
```jsx title="MyComponent.js"
import DocusaurusImageUrl from '@site/static/img/docusaurus.png';
<img src={DocusaurusImageUrl} />;
```
```jsx title="MyComponent.js"
<img src={require('@site/static/img/docusaurus.png').default} />
```
```jsx title="MyComponent.js"
import useBaseUrl from '@docusaurus/useBaseUrl';
<img src={useBaseUrl('/img/docusaurus.png')} />;
```
You can also import SVG files: they will be transformed into React components.
```jsx title="MyComponent.js"
import DocusaurusLogoWithKeytar from '@site/static/img/docusaurus_keytar.svg';
<DocusaurusLogoWithKeytar title="Docusaurus Logo" className="logo" />;
```
### In Markdown {#in-markdown}
In Markdown, you can stick to using absolute paths when writing links or images **in Markdown syntax** because Docusaurus handles them as `require` calls instead of URLs when parsing the Markdown. See [Markdown static assets](./guides/markdown-features/markdown-features-assets.mdx).
```md
You write a link like this: [Download this document](/files/note.docx)
Docusaurus changes that to: <a href={require('static/files/note.docx')}>Download this document</a>
```
:::caution use Markdown syntax
Docusaurus will only parse links that are in Markdown syntax. If your asset references are using the JSX tag `<a>` / `<img>`, nothing will be done.
:::
### In CSS {#in-css}
In CSS, the `url()` function is commonly used to reference assets like fonts and images. To reference a static asset, use absolute paths:
```css
@font-face {
font-family: 'Caroline';
src: url('/font/Caroline.otf');
}
```
The `static/font/Caroline.otf` asset will be loaded by the bundler.
:::warning important takeaway
One important takeaway: **never hardcode your base URL!** The base URL is considered an implementation detail and should be easily changeable. All paths, even when they look like URL slugs, are actually file paths.
If you find the URL slug mental model more understandable, here's a rule of thumb:
- Pretend you have a base URL like `/test/` when writing JSX so you don't use an absolute URL path like `src="/img/thumbnail.png"` but instead `require` the asset.
- Pretend it's `/` when writing Markdown or CSS so you always use absolute paths without the base URL.
:::
## Caveats {#caveats}
Keep in mind that:
- By default, none of the files in the `static` folder will be post-processed, hashed, or minified.
- However, as we've demonstrated above, we are usually able to convert them to `require` calls for you so they do get processed. This is good for aggressive caching and better user experience.
- Missing files referenced via hard-coded absolute paths will not be detected at compilation time and will result in a 404 error.
- By default, GitHub Pages runs published files through [Jekyll](https://jekyllrb.com/). Since Jekyll will discard any files that begin with `_`, it is recommended that you disable Jekyll by adding an empty file named `.nojekyll` file to your `static` directory if you are using GitHub pages for hosting.

View file

@ -0,0 +1,267 @@
---
description: A Docusaurus site is a pre-rendered single-page React application. You can style it the way you style React apps.
---
import ColorGenerator from '@site/src/components/ColorGenerator';
# Styling and Layout
:::tip
This section is focused on styling through stylesheets. For more advanced customizations (DOM structure, React code...), refer to the [swizzling guide](./swizzling.mdx).
:::
A Docusaurus site is a single-page React application. You can style it the way you style React apps.
There are a few approaches/frameworks which will work, depending on your preferences and the type of website you are trying to build. Websites that are highly interactive and behave more like web apps will benefit from more modern styling approaches that co-locate styles with the components. Component styling can also be particularly useful when you wish to customize or swizzle a component.
## Global styles {#global-styles}
This is the most traditional way of styling that most developers (including non-front-end developers) would be familiar with. It works fine for small websites that do not have much customization.
If you're using `@docusaurus/preset-classic`, you can create your own CSS files (e.g. `/src/css/custom.css`) and import them globally by passing them as an option of the classic theme.
```js title="docusaurus.config.js"
module.exports = {
// ...
presets: [
[
'@docusaurus/preset-classic',
{
// highlight-start
theme: {
customCss: [require.resolve('./src/css/custom.css')],
},
// highlight-end
},
],
],
};
```
Any CSS you write within that file will be available globally and can be referenced directly using string literals.
```css title="/src/css/custom.css"
.purple-text {
color: rebeccapurple;
}
```
```jsx
function MyComponent() {
return (
<main>
<h1 className="purple-text">Purple Heading!</h1>
</main>
);
}
```
If you want to add CSS to any element, you can open the DevTools in your browser to inspect its class names. Class names come in several kinds:
- **Theme class names**. These class names are listed exhaustively in [the next subsection](#theme-class-names). They don't have any default properties. You should always prioritize targeting those stable class names in your custom CSS.
- **Infima class names**. These class names are found in the classic theme and usually follow the [BEM convention](http://getbem.com/naming/) of `block__element--modifier`. They are usually stable but are still considered implementation details, so you should generally avoid targeting them. However, you can [modify Infima CSS variables](#styling-your-site-with-infima).
- **CSS module class names**. These class names have a hash in production (`codeBlockContainer_RIuc`) and are appended with a long file path in development. They are considered implementation details and you should almost always avoid targeting them in your custom CSS. If you must, you can use an [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) (`[class*='codeBlockContainer']`) that ignores the hash.
### Theme Class Names {#theme-class-names}
We provide some stable CSS class names for robust and maintainable global layout styling. These names are theme-agnostic and meant to be targeted by custom CSS.
:::tip
If you can't find a way to create a robust CSS selector, please [report your customization use-case](https://github.com/facebook/docusaurus/discussions/5468) and we will consider adding new class names.
:::
<details>
<summary>Exhaustive list of stable class names</summary>
```mdx-code-block
import ThemeClassNamesCode from '!!raw-loader!@site/../packages/docusaurus-theme-common/src/utils/ThemeClassNames.ts';
import CodeBlock from '@theme/CodeBlock';
<CodeBlock className="language-ts">
{ThemeClassNamesCode
// remove source comments
.replace(/\/\*[\s\S]*?\*\/|\/\/.*/g,'')
.replace(/^ *\n/gm,'')
.trim()}
</CodeBlock>
```
</details>
### Styling your site with Infima {#styling-your-site-with-infima}
`@docusaurus/preset-classic` uses [Infima](https://infima.dev/) as the underlying styling framework. Infima provides a flexible layout and common UI components styling suitable for content-centric websites (blogs, documentation, landing pages). For more details, check out the [Infima website](https://infima.dev/).
When you scaffold your Docusaurus project with `create-docusaurus`, the website will be generated with basic Infima stylesheets and default styling. You can override Infima CSS variables globally.
```css title="/src/css/custom.css"
:root {
--ifm-color-primary: #25c2a0;
--ifm-code-font-size: 95%;
}
```
Infima uses 7 shades of each color. We recommend using [ColorBox](https://www.colorbox.io/) to find the different shades of colors for your chosen primary color.
Alternatively, use the following tool to generate the different shades for your website and copy the variables into `/src/css/custom.css`.
<ColorGenerator />
### Dark Mode {#dark-mode}
In light mode, the `<html>` element has a `data-theme="light"` attribute; in dark mode, it's `data-theme="dark"`. Therefore, you can scope your CSS to dark-mode-only by targeting `html` with a specific attribute.
```css
/* Overriding root Infima variables */
[data-theme='dark'] {
--ifm-color-primary: #4e89e8;
}
/* Styling one class specially in dark mode */
[data-theme='dark'] .purple-text {
color: plum;
}
```
:::tip
It is possible to initialize the Docusaurus theme directly from a `docusaurus-theme` query string parameter.
Examples:
- [`https://docusaurus.io/?docusaurus-theme=dark`](https://docusaurus.io/?docusaurus-theme=dark)
- [`https://docusaurus.io/docs/configuration?docusaurus-theme=light`](https://docusaurus.io/docs/configuration?docusaurus-theme=light)
:::
### Mobile View {#mobile-view}
Docusaurus uses `996px` as the cutoff between mobile screen width and desktop. If you want your layout to be different in the mobile view, you can use media queries.
```css
.banner {
padding: 4rem;
}
/** In mobile view, reduce the padding */
@media screen and (max-width: 996px) {
.heroBanner {
padding: 2rem;
}
}
```
## CSS modules {#css-modules}
To style your components using [CSS Modules](https://github.com/css-modules/css-modules), name your stylesheet files with the `.module.css` suffix (e.g. `welcome.module.css`). Webpack will load such CSS files as CSS modules and you have to reference the class names as properties of the imported CSS module (as opposed to using plain strings). This is similar to the convention used in [Create React App](https://facebook.github.io/create-react-app/docs/adding-a-css-modules-stylesheet).
```css title="styles.module.css"
.main {
padding: 12px;
}
.heading {
font-weight: bold;
}
.contents {
color: #ccc;
}
```
```jsx
import styles from './styles.module.css';
function MyComponent() {
return (
<main className={styles.main}>
<h1 className={styles.heading}>Hello!</h1>
<article className={styles.contents}>Lorem Ipsum</article>
</main>
);
}
```
The class names will be processed by webpack into a globally unique class name during build.
## CSS-in-JS {#css-in-js}
:::caution
CSS-in-JS support is a work in progress, so libs like MUI may have display quirks. [Welcoming PRs](https://github.com/facebook/docusaurus/issues/1640).
:::
## Sass/SCSS {#sassscss}
To use Sass/SCSS as your CSS preprocessor, install the unofficial Docusaurus 2 plugin [`docusaurus-plugin-sass`](https://github.com/rlamana/docusaurus-plugin-sass). This plugin works for both global styles and the CSS modules approach:
1. Install [`docusaurus-plugin-sass`](https://github.com/rlamana/docusaurus-plugin-sass):
```bash npm2yarn
npm install --save docusaurus-plugin-sass sass
```
2. Include the plugin in your `docusaurus.config.js` file:
```js title="docusaurus.config.js"
module.exports = {
// ...
// highlight-next-line
plugins: ['docusaurus-plugin-sass'],
// ...
};
```
3. Write and import your stylesheets in Sass/SCSS as normal.
### Global styles using Sass/SCSS {#global-styles-using-sassscss}
You can now set the `customCss` property of `@docusaurus/preset-classic` to point to your Sass/SCSS file:
```js title="docusaurus.config.js"
module.exports = {
presets: [
[
'@docusaurus/preset-classic',
{
// ...
theme: {
// highlight-next-line
customCss: [require.resolve('./src/css/custom.scss')],
},
// ...
},
],
],
};
```
### Modules using Sass/SCSS {#modules-using-sassscss}
Name your stylesheet files with the `.module.scss` suffix (e.g. `welcome.module.scss`) instead of `.css`. Webpack will use `sass-loader` to preprocess your stylesheets and load them as CSS modules.
```scss title="styles.module.scss"
.main {
padding: 12px;
article {
color: #ccc;
}
}
```
```jsx
import styles from './styles.module.scss';
function MyComponent() {
return (
<main className={styles.main}>
<article>Lorem Ipsum</article>
</main>
);
}
```

Some files were not shown because too many files have changed in this diff Show more