ブログ内のリンクを Next.js の <Link> や Embed に変換したい
この記事は最終更新日から2年以上が経過しています。
概要
このブログ内のリンクを Next.js の <Link>
や Twitter の Embed に変換したいので対応した
結論
- rehype-react を使ってReactコンポーネントと置き換える
- Twitter埋め込みはreact-twitter-embedを使う
埋め込むとこんな感じ
方法
Markdownをrehypeで読み込んだものを置き換える
rehype-react を導入
$ yarn add rehype-react
Markdown -> HTML 変換したものをさらに Reactコンポーネントに変換する
dangerouslySetInnerHTML に流し込んでいるだけの部分を以下のように変更して、Reactにさらなる変換をかける
// post-body.tsx
+import { processor } from '../../../lib/htmlToReact'
import markdownStyles from './markdown-styles.module.css'
type Props = {
content: string
}
const PostBody = ({ content }: Props) => {
return (
<div className="max-w-2xl mx-auto">
- <div className={markdownStyles['markdown']}
- dangerouslySetInnerHTML={{ __html: content }}
- />
+ <div className={markdownStyles['markdown']}>
+ {processor.processSync(content).result}
+ </div>
</div> )
}
export default PostBody
Reactに変換する部分の本体は以下のような感じ
fragment: true
にしておかないとheadとかbodyが挿入されてしまうので、必ずtrueにしておくこと
// htmlToReact.ts
import { createElement } from 'react'
import rehypeParse from 'rehype-parse'
import rehypeReact from 'rehype-react'
import { unified } from 'unified'
import CustomLink from '../components/headless/custom-link'
export const processor = unified()
.use(rehypeParse, { fragment: true }) // fragmentは必ずtrueにする
.use(rehypeReact, {
createElement
})
a-tagを独自コンポーネントに入れ替える
react-twitter-embed の導入
Twitterのembedは普通にやるとうまくいかないので react-twitter-embed を使う
$ yarn add rehype-react
独自コンポーネントへの入れ替え
rehypeReact でどのタグをどのコンポーネントに変換するかを設定する
// htmlToReact.ts
import { createElement } from 'react'
import rehypeParse from 'rehype-parse'
import rehypeReact from 'rehype-react'
import { unified } from 'unified'
+import CustomLink from '../components/headless/custom-link'
export const processor = unified()
.use(rehypeParse, { fragment: true }) // fragmentは必ずtrueにする
.use(rehypeReact, {
createElement,
+ components: {
+ a: CustomLink
+ }
})
変換するコンポーネントはこんな感じ。 必要に応じてLinkに変更したり、embedしたりする。
import Link from 'next/link'
import { FC, VFC } from 'react'
import { TwitterTweetEmbed } from 'react-twitter-embed'
type Props = {
href: string
}
const Embed: VFC<Props> = ({ href }) => {
if (href.includes('https://twitter.com/')) {
const url = new URL(href)
const paths = url.pathname.split('/')
const tweetId = paths[paths.length - 1]
return <TwitterTweetEmbed tweetId={tweetId} />
}
return (
<a href={href} target="_blank" rel="noopener noreferrer">
{href}
</a>
)
}
const CustomLink: FC<Props> = ({ children, href }) => {
if (children === undefined || children === null || children === '') {
// a-tagの中身が空ならembedに変換する
return <Embed href={href} />
}
// a-tagに中身がある場合は必要に応じてLinkに変換する
return href.startsWith('/') || href === '' ? (
<Link href={href}>
<a>{children}</a>
</Link> ) : (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
)
}
export default CustomLink