Getting Started
To get started quickly with an example project you can use npm init
. It will scaffold out a Next.js app with MDX configured.
npm init mdx
Components
You can pass in components for any HTML element that Markdown compiles to. This allows you to use your existing components and even CSS-in-JS like styled-components
.
The components object is a mapping between the HTML element and your desired component you’d like to render.
const MyH1 = props => <h1 style={{ color: 'tomato' }} {...props} /> const MyParagraph = props => <p style={{ fontSize: '18px', lineHeight: 1.6 }} /> const components = { h1: MyH1, p: MyParagraph }
Example usage
import React from 'react' import Hello from '../hello.md' import { Text, Heading, Code, InlineCode } from '../ui' export default () => <Hello components={{ h1: Heading, p: Text, code: Code, inlineCode: InlineCode }} />
With the above, the Heading
component will be rendered for any h1
, Text
for p
elements, and so on.
In addition to HTML elements, there’s an inlineCode
. This is what remark uses for code elements within paragraphs, tables, etc.
MDXProvider
If you’re using an app layout that wraps your JSX, you can use the MDXProvider
to only pass your components in one place:
import React from 'react' import { MDXProvider } from '@mdx-js/tag' import { Heading, Text, Pre, Code, Table } from './components' const components = { h1: Heading.H1, h2: Heading.H2, // ... p: Text, code: Pre, inlineCode: Code } export default props => <MDXProvider components={components}> <main {...props} /> </MDXProvider>
This allows you to remove duplicated component imports and passing. It will typically go in layout files.
How it works
MDXProvider uses React Context to provide the component mapping to MDXTag. This way MDXTag knows that it should override the standard components with those provided in this mapping.
Because MDXProvider uses React Context directly, it is affected by the same caveats. It is therefore important that you do not declare your components mapping inline in the JSX. Doing so will trigger a rerender of your entire MDX page with every render cycle. Not only is this bad for performance, but it can cause unwanted side affects, like breaking in-page browser navigation.
Avoid this by following the pattern shown above and declare your mapping as a constant.
Table of components
The following components are rendered from Markdown, so these can be keys in the component object you pass to MDXProvider.
Tag | Name | Syntax |
---|---|---|
p | Paragraph | |
h1 | Heading 1 | # |
h2 | Heading 2 | ## |
h3 | Heading 3 | ### |
h4 | Heading 4 | #### |
h5 | Heading 5 | ##### |
h6 | Heading 6 | ###### |
thematicBreak | Thematic break | *** |
blockquote | Blockquote | > |
ul | List | - |
ol | Ordered list | 1. |
li | List item | |
table | Table | --- | --- | --- |
tr | Table row | This | is | a | table row |
td /th | Table cell | |
pre | Pre | |
code | Code | |
em | Emphasis | _emphasis_ |
strong | Strong | **strong** |
delete | Delete | ~~strikethrough~~ |
code | InlineCode | |
hr | Break | --- |
a | Link | <https://mdxjs.com> or [MDX](https://mdxjs.com) |
img | Image | ![alt](https://mdx-logo.now.sh) |
Updating the mapping object during application runtime
If you need to change the mapping during runtime, declare it on the componentʼs state object:
import React from 'react' import { MDXProvider } from '@mdx-js/tag' import { Heading, Text, Pre, Code, Table } from './components' export default class Layout extends React.Component { state = { h1: Heading.H1, h2: Heading.H2, // ... p: Text, code: Pre, inlineCode: Code } render() { return ( <MDXProvider components={this.state}> <main {...this.props} /> </MDXProvider> ) } }
You can now use the setState
function to update the mapping object and be assured that it wonʼt trigger unnecessary renders.
Projects, libraries and frameworks
If you’re already working with a particular tool, you can try out MDX with the following commands: