Guides / Injecting components
This guide shows how to inject arbitrary components into MDX when it runs. It shows how the underlying features used by our providers (@mdx-js/react
, @mdx-js/preact
) and the mdx-components.tsx
file supported by Next.js work, and how you can take advantage of that functionality yourself.
In many cases you do not need this, as you can pass components to MDX:
# Hello *<Planet />*
You can pass Planet
and say a component used instead of the h1
:
import Example from './example.mdx' // Assumes an integration is used to compile MDX -> JS.
console.log(
<Example
components={{
Planet() {
return 'Pluto'
},
h1(properties) {
return <h2 {...properties} />
}
}}
/>
)
(alias) function Example(props: MDXProps): Element
import Example
An function component which renders the MDX content using JSX.
props
inside the MDX component.namespace console
var console: Console
The console
module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.
The module exports two specific components:
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.console
instance configured to write to process.stdout
and process.stderr
. The global console
can be used without calling require('console')
.Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O
for more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
(method) Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
(alias) function Example(props: MDXProps): Element
import Example
An function component which renders the MDX content using JSX.
props
inside the MDX component.(property) MDXProps.components?: MDXComponents
This prop may be used to customize how certain components are rendered.
(method) Planet(): string
(property) h1?: Component<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(property) JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
When you find yourself passing that components
prop around a lot, you might want to look at an alternative. You might reach for our context based providers (@mdx-js/react
, @mdx-js/preact
), but context has performance downsides and context doesn’t always work (such as in RSC).
But first, how does component passing work? That can be illustrated by looking at the code generated by MDX for the above example.mdx
. Here is a diff that shows what the example normally compiles to and what changes when providerImportSource: 'xxx'
is passed:
@@ -1,7 +1,13 @@
import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
+import {useMDXComponents as _provideComponents} from 'xxx'
function _createMdxContent(props) {
- const _components = {em: 'em', h1: 'h1', ...props.components}
+ const _components = {
+ em: 'em',
+ h1: 'h1',
+ ..._provideComponents(),
+ ...props.components
+ }
const {Planet} = _components
if (!Planet) _missingMdxReference('Planet', true)
return _jsxs(_components.h1, {
@@ -10,7 +16,7 @@ function _createMdxContent(props) {
}
export default function MDXContent(props = {}) {
- const {wrapper: MDXLayout} = props.components || {}
+ const {wrapper: MDXLayout} = {..._provideComponents(), ...props.components}
return MDXLayout
? _jsx(MDXLayout, {...props, children: _jsx(_createMdxContent, {...props})})
: _createMdxContent(props)
Observe that components have defaults (such as that h1
will use 'h1'
) and that components are taken from props.components
. What changes is an added call to _provideComponents
, which refers to an useMDXComponents
export from the module we specified (xxx
).
We can use this interface to inject components from a file. In that file, we need a useMDXComponents
function that returns our components.
/**
* @import {MDXComponents} from 'mdx/types.js'
*/
/** @returns {MDXComponents} */
export function useMDXComponents() {
return {
Planet() {
return 'Pluto'
},
h1(properties) {
return <h2 {...properties} />
}
}
}
function useMDXComponents(): MDXComponents
(method) Planet(): string
(property) h1?: Component<React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(property) JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
(parameter) properties: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
And now passing a file path or URL to that file as providerImportSource
, such as with import.meta.resolve('./mdx-components.js')
:
@@ -1,5 +1,5 @@
import {jsx as _jsx, jsxs as _jsxs} from 'react/jsx-runtime'
-import {useMDXComponents as _provideComponents} from 'xxx'
+import {useMDXComponents as _provideComponents} from 'file:///Users/tilde/…/mdx-components.js'
Now our locally defined components will be used in all MDX files!