Importing a component into an MDX file provides it to the
MDX content in a different way than using MDXProvider
context. Let's say Button
is a shortcode instead of an
import and thus coming from an MDXProvider
. This means
it's not being imported anywhere and as a result is not
available in useMDXScope
from gatsby-plugin-mdx
. Note
the following MDX which is the same as
the useMDXScope
post,
except we're using Button
as a shortcode so we don't use
the import.
# Something<Button text="Whatever"/>describing some stuff```js react-live<Button text="whatever" />```and saying some more for the docs
Shortcodes are implemented by using an MDXProvider
to
provide React components as such:
<MDXProvidercomponents={{Button: props => <button>my button</button>}}>{app}</MDXProvider>
So let's get set up here. Using the same template in
gatsby-browser.js
as our last post, we've made a few
adjustments.
import React from 'react'import { MDXProvider } from '@mdx-js/react'import Highlight, { defaultProps } from 'prism-react-renderer'import { LiveProvider, LiveEditor, LiveError, LivePreview } from 'react-live'import { preToCodeBlock } from 'mdx-utils'// import {useMDXScope} from 'gatsby-plugin-mdx/context'import { useMDXComponents } from '@mdx-js/react'const Code = ({ codeString, language, ...props }) => {const components = useMDXComponents()if (props['react-live']) {return (<LiveProvidercode={codeString}scope={components}><LiveEditor /><LiveError /><LivePreview /></LiveProvider>)} else {return (<Highlight {...defaultProps} code={codeString} language={language}>{({ className, style, tokens, getLineProps, getTokenProps }) => (<pre className={className} style={style}>{tokens.map((line, i) => (<div {...getLineProps({ line, key: i })}>{line.map((token, key) => (<span {...getTokenProps({ token, key })} />))}</div>))}</pre>)}</Highlight>)}}// components is its own object outside of render so that the references to// components are stableconst components = {Button: props => Button: props => <button>my button</button>,pre: preProps => {const props = preToCodeBlock(preProps)// if there's a codeString and some props, we passed the testif (props) {return <Code {...props} />} else {// it's possible to have a pre without a code in itreturn <pre {...preProps} />}},}export const wrapRootElement = ({element}) => {return <MDXProvider components={components}>{element}</MDXProvider>}
The important chage here is the import from @mdx-js/react
.
import { useMDXComponents } from "@mdx-js/react";
Which we then use to replace our usage of useMDXScope
.
(note: it's perfectly fine to keep both hooks and merge them
together, we'll talk about this in the finale of this series
of posts)
const components = useMDXComponents();
Finally, since we said we're using shortcodes, we need a
shortcode to use. In this case we chose to use Button
, so
we'll hard-code a button
that shows the text my button
.
const components = {Button: props => Button: props => <button>my button</button>,pre: preProps => {const props = preToCodeBlock(preProps)// if there's a codeString and some props, we passed the testif (props) {return <Code {...props} />} else {// it's possible to have a pre without a code in itreturn <pre {...preProps} />}},}
This results in our markdown code blocks being able to use components that were provided as shortcodes, potentially making documentation easier.