May 11, 2020
MDX lets us use React Components in Markdown files using next-generation tooling. Fenced Code Blocks have some unique extensibility points for exposing new functionality from our .mdx
files.
From the MDX site,
MDX is an authorable format that lets you seamlessly write JSX in your Markdown documents. You can import components, such as interactive charts or alerts, and embed them within your content. This makes writing long-form content with components a blast ๐.
Markdown lets us write documentation in human-readable and computer-readable formats. Simple formatting is converted to HTML.
A simple Markdown (.md
) file may look like this:
md1#### Hello, _world_!23This is an example of text in Markdown.45- Red6- Green7- Blue
This is an example of text in Markdown.
A simple MDX (.mdx
) file may look like this:
mdx1#### Hello, _world_!23Below is an example of JSX embedded in Markdown.45<div style={{ padding: "20px", backgroundColor: "#B71C1C" }}>6 <h3>This is JSX</h3>7</div>
Below is an example of JSX embedded in Markdown.
MDX can be great, but as you can see, we can lose some of the flexibility and readability markdown has made us believe.
Take the following .mdx
:
mdx1import BundlephobiaInline from "bundlephobia-inline";23<BundlephobiaInline packageName="react-query" />4<BundlephobiaInline packageName="react-table" />5<BundlephobiaInline packageName="react" />6<BundlephobiaInline packageName="react-dom" />7<BundlephobiaInline packageName="dayjs" />8<BundlephobiaInline packageName="styled-components" />
This has turned a simple list of packages into an overly verbose list of code. I'd much rather use something like this
```bundlephobiareact-queryreact-tablereactreact-domdayjsstyled-components```
Using the <MDXProvider components={components} />
, we can customize any code
blocks with a custom React component.
jsx1import { MDXProvider } from "@mdx-js/react";23const components = {4 pre: (props) => <div {...props} />,5 code: Code,6};
Let's take a look at how <Code />
works, and what props get passed.
```bundlephobia include-links=truereact-query```
children
as the contentreact-query
className
with language-*
language-bundlephobia
metastring
withWith a little trickery, we can overload the language-
value being passed in to short-circuit the Code
return value for the unique scenarios:
jsx1function Code(props) {2 const { children: codeString = "", metastring = "", className = "" } = props;3 const language = className.replace(/language-/, "");45 //6 // If we see "bundlephobia" fenced code block7 //8 if (["bundlephobia"].includes(language)) {9 const lines = codeString.trim().split(/[\n\r]+/);10 return (11 <>12 {lines.map((line) => (13 <div>14 <Bundlephobia key={line} packageName={line} />15 </div>16 ))}17 </>18 );19 }2021 // ...22}
Here's it running in action:
bundlephobia1react-query2react-table3react4react-dom5dayjs6styled-components
chart// ```chart type=barGreen Team=[1,2,3]Blue Team=[3,4,2]
vs
jsx1<Chart2 records={[3 { label: "Green Team", data: [1, 2, 3] },4 { label: "Blue Team", data: [3, 4, 2] },5 ]}6/>
map// ```mapGroom Lake, Nevada
vs
jsx<Map address="Groom Lake, Nevada" />
Further reading...