2024-05-10
#Next.jsMDXRemote 為標題加上錨點
本篇文章使用 Next.js App router 及 MDXRemote
#說明
假設我們的 markdown 長這樣
# 這是大標題
內文內文內文....
結果會長這樣
<h1>這是大標題</h1>
<p>內文內文內文....</p>
而這是我們希望的樣子
<a href="#unique-id">
<h1 id="unique-id">這是大標題</h1>
</a>
<p>內文內文內文....</p>
#需要使用兩個套件
要達到希望的樣子,我們需要:
- 給標題一個 id:使用
rehype-slug
- 加上連結:使用
rehype-autolink-headings
,他會把有 id 的標題都加上連結
#為標題加上 id
安裝 rehype-slug
yarn add rehype-slug
使用 rehype-slug
import rehypeSlug from 'rehype-slug';
// ...
const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [rehypeSlug],
},
};
// ...
export default function Markdown() {
return <MDXRemote source={data} options={options} />;
}
檢查:我們打開 f12
看看元素,會發現標題都多了一個 id 屬性了。
#加上連結
安裝 rehype-autolink-headings
yarn add rehype-autolink-headings
使用
使用方法及參數詳見這邊。
稍微說明幾個參數:
behavior
:可以是'after' | 'append' | 'before' | 'prepend' | 'wrap'
,代表連結要放在哪邊,例如wrap
,代表要包住標題;使用before
則會把連結放在標題元素前。content
:連結元素的內容,預設會是<span class="icon icon-link"></span>
。我們也可以自訂,如下面程式碼,就是自訂為<span class="text-tertiary text-3xl"></span>
。
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
// ...
const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: 'prepend',
content: {
type: 'element',
tagName: 'span',
properties: { class: 'text-tertiary text-3xl' },
children: [{ type: 'text', value: '#' }],
},
},
],
],
},
};
// ...
export default function Markdown() {
return <MDXRemote source={data} options={options} />;
}
簡單兩步驟,就完成啦🎉🎉🎉
#補充:自訂 rehype
也許你不想在所有 headings
都加上連結。像在這個部落格中,只有 h2
有連結,要如何做到呢?
我們可以使用 unist-util-visit
套件,它會遍歷語法樹並修改樹中的節點。
安裝 unist-util-visit
yarn add unist-util-visit
自訂 function
import { visit } from 'unist-util-visit';
// ...
function addIdToH2() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'h2') {
if (!node.properties.id) {
const id = node.children
.filter((child) => child.type === 'text')
.map((child) => child.value)
.join(' ')
.toLowerCase()
.replace(/\s+/g, '-');
node.properties.id = id;
}
}
});
};
}
在頁面中使用
const options = {
mdxOptions: {
remarkPlugins: [],
rehypePlugins: [
addIdToH2,
],
},
};