MDX lets you use JSX in markdown content and with the help of rehype and remark plugins, you can create custom components and add additional functionality to your markdown content. This page shows some custom MDX components I created to add some custom visual elements to my mdx based blog posts.
Credits and thanks to countless sources for the inspiration and code snippets used to create these components.
I am using a vertical split layout to show the raw markdown content and the rendered content. The top section shows the raw markdown and the bottom section shows the rendered content.
Here is an example split layout:
*Markdown* **content** with `inline code`.
Markdown content with inline code
.
h1 - h4 headings are supported. Like standard markdown, the number of #
characters at
the beginning of the line determines the heading level.
Unique slugs are automatically generated for each heading using the rehype-slug plugin.
Anchor tags are automatically generated and for each heading using the
rehype-autolink-headings plugin.
The unique slug is used as the anchor tag id
.
A custom rehype plugin is used to automatically generate the table of contents from the
headings. An IntersectionObserver
is used to highlight the current heading in the
table of contents.
Table of contents is only visible on screen / windows wider than 1280px. If you are reading this on a wider screen, you should see the table of contents on the right side of the page. -->
Code blocks, inline code, and code snippets are syntax highlighted using a combination of rehype-pretty-code and a custom rehype plugin, both powered by shiki.style.
Text with `inline code`.
Text with inline code
.
The language for syntax highlighting is detected from the code block meta information.
```python
def hello_world():
print("Hello, World!")
```
def hello_world():
print("Hello, World!")
A copy button is automatically added to each code block. The button is only visible on hover. Code blocks usually take up full width, so the button rarely overlaps with the code. But in the sandbox split layout, the button may overlap with the code.
A code block header is automatically added to the code block if a file name is provided
using the fileName
attribute. An icon is also added to the header based on the language.
```javascript showLineNumbers fileName="example.js"
export const example = () => {
console.log("JS code block with file name and line numbers");
}
```
export const example = () => {
console.log("JS code block with file name and line numbers");
}
Footnotes in code blocks are supported using a custom shiki.style transformer.
```python showLineNumbers fileName="example.py"
def example():
print("footnote") # [!code footnote]
print("another footnote") # [!code footnote]
```
1. This is a code footnote.
2. This is the second code footnote.
There is a zero space white space character just before
the `]` in the footnote code. I had to add this to escape
the footnote code from the markdown parser for the purpose of
this demo. This must NOT be in the actual markdown file.
def example():
print("footnote")
print("another footnote")
Line highlighting, line ranges, and line numbers are supported by functionality provided by rehype-pretty-code. Line numbers are automatically hidden on small screens.
By default, the code snippet highlighting assumes the language is shell
. The language
can be changed by providing a lang
prop.
Code snippet by default also take up full width. The width can be changed by providing a
width
prop. A fit-content
value can also be provided to make the code snippet width
fit the content.
<CodeSnippet text="npm i lodash" width="fit-content"/>
npm i lodash
| Column A | Column B | Column C |
|----------|----------|----------|
| A1 | B1 | C1 |
| A2 | B2 | C2 |
| A3 | B3 | C3 |
Column A | Column B | Column C |
---|---|---|
A1 | B1 | C1 |
A2 | B2 | C2 |
A3 | B3 | C3 |
Standard markdown tables are supported. But we can also wrap the standard markdown table
in a Table
component to add additional functionality like custom column widths and
captions.
caption
and captionStyle
props can be provided to add a caption to the table. The
captionStyle
prop can be used to style the caption. The captionSide
property can be
used to set the position of the caption.
columnWidths
prop can be used to set the width of each column. The width of each column
is set as a percentage of the total table width. The columnWidths
prop takes a string
with space separated values. The number of values should match the number of columns in
the table.
<Table
caption="Table 1: Sample Table"
captionStyle={{captionSide: "bottom", fontStyle: "italic"}}
columnWidths="50 25 25"
>
| Column A | Column B | Column C |
|----------|----------|----------|
| A1 | B1 | C1 |
| A2 | B2 | C2 |
| A3 | B3 | C3 |
</Table>
Column A | Column B | Column C |
---|---|---|
A1 | B1 | C1 |
A2 | B2 | C2 |
A3 | B3 | C3 |