SY

6 min
mdxnextjs

MDX Components

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.

Headings

h1 - h4 headings are supported. Like standard markdown, the number of # characters at the beginning of the line determines the heading level.

Unique Heading Slugs

Unique slugs are automatically generated for each heading using the rehype-slug plugin.

Anchor Tags

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.

Table of Contents

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

Syntax Highlighting

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.

Inline Code

Text with `inline code`.

Text with inline code.

Code Block

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!")

Copy Button

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.

File Name with Icon

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");
}
```
example.js
export const example = () => {
    console.log("JS code block with file name and line numbers");
}

Code Footnotes

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.
example.py
def example():
    print("footnote")  
    print("another footnote")  
  1. This is a code footnote.
  2. This is the second code footnote.

Other Features

Line highlighting, line ranges, and line numbers are supported by functionality provided by rehype-pretty-code. Line numbers are automatically hidden on small screens.

Code Snippet

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

Tables

Standard Markdown Table

| Column A | Column B | Column C |
|----------|----------|----------|
| A1       | B1       | C1       |
| A2       | B2       | C2       |
| A3       | B3       | C3       |
Column AColumn BColumn C
A1B1C1
A2B2C2
A3B3C3

Custom Table Component

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>
Table 1: Sample Table
Column AColumn BColumn C
A1B1C1
A2B2C2
A3B3C3

Credits