This is an simple example for custom styled MDX.
We will integrate MDX with following modules:
- Create React App for build environment
- Material UI v4 components
- Syntax highlighted with Prism
Eventually, it's aim to render the markdown source to this.
Execute following command to create a React app:
npx create-react-app my-app
cd my-app
Execute following command to install mdx.macro
:
yarn add mdx.macro
Then create the following src/App.js
:
// src/App.js
import React, { lazy, Suspense } from 'react';
import { importMDX } from 'mdx.macro';
const Content = lazy(() => importMDX('./Content.mdx'));
const App = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<Content />
</Suspense>
</div>
);
export default App;
And then create the following src/Content.mdx
:
# Hello, world!
🔗 Reference: Getting Started - Create React App
Install Material UI:
yarn add -D @material-ui/core@next
Add CSS Baseline:
import React, { lazy, Suspense } from 'react';
import { importMDX } from 'mdx.macro';
+ import CssBaseline from '@material-ui/core/CssBaseline';
const Content = lazy(() => importMDX('./Content.mdx'));
const App = () => (
- <div>
- <Suspense fallback={<div>Loading...</div>}>
- <Content />
- </Suspense>
- </div>
+ <>
+ <CssBaseline />
+ <div>
+ <Suspense fallback={<div>Loading...</div>}>
+ <Content />
+ </Suspense>
+ </div>
+ </>
);
export default App;
Add Roboto Font into public/index.html
:
<title>React App</title>
+ <link
+ rel="stylesheet"
+ href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
+ />
</head>
Import MDXProvider
:
import React, { lazy, Suspense } from 'react';
import { importMDX } from 'mdx.macro';
import CssBaseline from '@material-ui/core/CssBaseline';
+ import { MDXProvider } from '@mdx-js/tag';
+ import components from './components';
const Content = lazy(() => importMDX('./Content.mdx'));
const App = () => (
<>
<CssBaseline />
- <div>
- <Suspense fallback={<div>Loading...</div>}>
- <Content />
- </Suspense>
- </div>
+ <MDXProvider components={components}>
+ <div>
+ <Suspense fallback={<div>Loading...</div>}>
+ <Content />
+ </Suspense>
+ </div>
+ </MDXProvider>
</>
);
export default App;
Notice: if you are using mdx-loader
, you might have to use @mdx-js/react
instead.
Set components:
// src/components.js
import React, { memo } from 'react';
import Typography from '@material-ui/core/Typography';
const components = {
h1: (() => {
const H1 = props => <Typography {...props} component="h1" variant="h1" />;
return memo(H1);
})(),
};
export default components;
You can see that your contents with MDX are applied Material UI components now.
Try to customize all components. You can check the table of components to know the tags to change.
You can also customize the wrapper. It's great to add a class name to handle the gutter between components like markdown.css inspired from github-markdown-css.
You can use the markdown-it
demo code to preview the result.
You can use prism-react-renderer
directly:
yarn add prism-react-renderer
Create a CodeBlock
component:
// src/CodeBlock.js
import React from 'react';
import Highlight, { defaultProps } from 'prism-react-renderer';
export default ({ children, className }) => {
const language = className.replace(/language-/, '');
return (
<Highlight {...defaultProps} code={children} language={language}>
{({ className, style, tokens, getLineProps, getTokenProps }) => (
<pre className={className} style={{ ...style, padding: '20px' }}>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line, key: i })}>
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token, key })} />
))}
</div>
))}
</pre>
)}
</Highlight>
);
};
Add CodeBlock
to MDXProvider
:
import React, { memo } from 'react';
import Typography from '@material-ui/core/Typography';
+ import CodeBlock from './CodeBlock';
const components = {
h1: (() => {
const H1 = props => <Typography {...props} component="h1" variant="h1" />;
return memo(H1);
})(),
+ code: CodeBlock,
};
export default components;
You can use syntax highlighted code block now!
- Pros: easy
- Cons: Prism components are not split
You can customized your own CodeBlock
with code splitting like this. It will load only main prism at start and load other components only if they are required. The code would be highlighted after the required prism components loaded.
You can check the completed components here.