Next.js×Markdownブログでリンクを自動判別!外部リンクを別タブで開く実装ガイド

みなさん、こんにちは!WEB工房しずおかの開発チームです。
Next.jsとMarkdownを利用してブログやオウンドメディアを構築していると、記事内の「リンク」の挙動について悩むことはありませんか?
標準のMarkdown記法([テキスト](URL))でそのままリンクを出力すると、自分のサイト内の別の記事(内部リンク)も、外部サイト(外部リンク)もすべて同じタブで開いてしまいます。
ユーザー体験を考えると、自分のサイト内の回遊は同じタブで高速に遷移し、外部サイトへ飛ぶときだけ新しいタブ(別タブ)で開きたいというのが一般的ですよね。
今回は、Next.jsと react-markdown を使ったブログ環境において、リンクの種類(外部リンク・内部リンク・YouTube動画)を自動で判別して最適なタグを出力するカスタマイズ方法を解説します!
ユーザーの離脱を防ぎ、回遊率を上げたい方はぜひ参考にしてみてください。
要件
- Markdown内のリンク(
aタグ)の出力をカスタマイズしたい - 外部サイトへのリンクは
target="_blank"で別タブで開くようにする - サイト内のリンクは Next.jsの
<Link>コンポーネントを使って高速にページ遷移させる - (おまけ)YouTubeのURLが貼られた場合は自動で動画プレイヤー(iframe)に変換する
目標
react-markdownのcomponentsプロパティを使ってaタグを上書きする- 渡された
hrefの文字列を判定して出力を分岐させる - 記事執筆者はMarkdownに普通にURLを書くだけで、システム側が自動処理してくれる状態にする
↓ react-markdown の詳細はこちら(外部サイトですが、今回の実装を行うと別タブで開くようになります)↓
https://github.com/remarkjs/react-markdown
実際に試してみる
必要なもの
- Next.jsで構築されたプロジェクト
- ブログのレンダリングに
react-markdownを使用しているファイル(例:src/app/blog/[id]/page.tsxなど)
手順
記事をレンダリングしているコンポーネント内で、react-markdown の components に渡すカスタムコンポーネントを定義します。
今回は**「リンク部分(aタグ)のカスタマイズ」**に絞ってコードを解説します。
import Link from 'next/link';
// ▼▼▼ カスタムコンポーネント ▼▼▼
const customComponents = {
// aタグの自動判別機能
a: (props: any) => {
const { href, children } = props;
// hrefが空の場合はそのまま返却
if (!href) return <a {...props}>{children}</a>;
// 1. YouTubeのURLかどうか判定し、iframeに変換
const ytMatch = href.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/);
if (ytMatch) {
const videoId = ytMatch[1];
return (
<span className="uVideoResponsive" style={{ display: 'block' }}>
<iframe
src={`https://www.youtube.com/embed/${videoId}`}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen>
</iframe>
</span>
);
}
// 2. 外部リンクの判定(http:// または https:// で始まるか)
const isExternal = href.startsWith('http://') || href.startsWith('https://');
if (isExternal) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
style={{ color: '#0070f3', textDecoration: 'underline' }}
>
{children}
</a>
);
}
// 3. 内部リンクの場合(Next.js の Link コンポーネントを使用して高速遷移)
return (
<Link href={href} style={{ color: '#0070f3', textDecoration: 'underline' }}>
{children}
</Link>
);
}
};
// ▲▲▲ ここまでカスタムコンポーネント ▲▲▲
これを定義したら、あとは <ReactMarkdown components={customComponents}> のように渡すだけで完了です。
記述内容を1つずつ確認していきます。
【STEP1:YouTube動画の自動埋め込み】
const ytMatch = href.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/);
if (ytMatch) { ... }
まずは正規表現を使って、リンクがYouTubeのURLかどうかをチェックしています。
もしYouTubeのURLだった場合は、Markdownのテキストリンクを出力するのではなく、<iframe> タグを生成して動画プレイヤーとして表示させています。これで記事内にYouTubeのURLをコピペするだけで、リッチな動画埋め込みが完成します。
【STEP2:外部リンクを別タブで開く】
const isExternal = href.startsWith('http://') || href.startsWith('https://');
if (isExternal) {
return (
<a href={href} target="_blank" rel="noopener noreferrer">
{children}
</a>
);
}
URLが http:// や https:// から始まっている場合は、外部サイトへのリンクとみなします。
この時、target="_blank" を付与することで新しいタブで開くようにし、同時にセキュリティ対策として rel="noopener noreferrer" を必ずセットで付与しています。
【STEP3:内部リンクはNext.jsの機能で高速化】
return (
<Link href={href}>
{children}
</Link>
);
上記のどちらにも当てはまらない場合(例:/blog/category/tech のような相対パスなど)は、自分のサイト内のリンクと判断します。
ここでは通常の <a> タグではなく、Next.jsの <Link> コンポーネントを使用します。これにより、ブラウザのフルリロードを挟むことなく、SPA(シングルページアプリケーション)のように一瞬で次の記事へ遷移させることができます。
【💡 セキュリティ上の注意点】
target="_blank"を使用して別タブでリンクを開く際は、遷移先のページから元のページを操作されるリスク(Tabnabbing攻撃)を防ぐために、必ずrel="noopener noreferrer"を併記しましょう。
まとめ:Markdownの表現力を拡張して快適なブログを作ろう
今回は、Next.jsと react-markdown の環境で、リンクの種類に応じて出力を自動で最適化するコードを解説しました。
この設定を入れておけば、記事を書くたびにHTMLタグを手打ちしたり、別タブ設定を意識したりする必要がなくなります。
- 外部リンクの離脱防止(別タブ化)
- 内部リンクの高速化
- YouTubeの自動埋め込みによるリッチ化
など、読者にとっても執筆者にとってもメリットが大きいカスタマイズです。
開発チームでは、大規模Webシステムからオウンドメディアの構築まで、細かなユーザー体験の向上にこだわった開発を日々進めております。自社サイトやWebシステムの運用改善でお悩みの方は、ぜひこのような細かなカスタマイズにも挑戦してみてください!