🐦
Next.jsでツイートを埋め込む
Next.jsでTwitter上の投稿を埋め込む方法を解説します。
手順
ページコンポーネントでnext/script
を使ってwidgets.js
を読み込み、ツイートを表示したいコンポーネントでwidgets.js
が認識してくれる埋め込みコードを挿入すればOKです。
pages/index.tsx
import React from 'react';
import Script from 'next/script';
import { Tweet } from '../components/Tweet';
const HomePage: React.FC = () => (
<>
<Tweet id="20" />
<Script
src="https://platform.twitter.com/widgets.js"
strategy="lazyOnload"
/>
</>
);
export default HomePage;
components/Tweet.tsx
import React, { useEffect, useRef } from 'react';
export const Tweet: React.FC<{ id: string }> = ({ id }) => {
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
// @ts-expect-error
window.twttr?.widgets.load(ref.current);
}, [id]);
return (
<div
dangerouslySetInnerHTML={{ __html: generateEmbedHtml(id) }}
ref={ref}
/>
);
};
const generateEmbedHtml = (id: string): string => {
if (!/^\d+$/u.test(id)) {
throw new Error(`Invalid tweet ID: ${id}`);
}
return `<blockquote class="twitter-tweet"><a href="https://twitter.com/i/status/${id}"></a></blockquote>`;
};
実装上の注意点
実装にあたり注意が必要な罠が2つあります。
dangerouslySetInnerHTML
を使う必要がある
1. widgets.js
はDOMツリー上で.twitter-tweet
の要素を書き換えてしまうので、それらの要素はdangerouslySetInnerHTML
で挿入しReactの管理下から外す必要があります。
// ⭕ OK
<div
dangerouslySetInnerHTML={{ __html: `...` }}
ref={ref}
/>
// ❌ NG
<div ref={ref}>
<blockquote className="twitter-tweet">
<a href={`https://twitter.com/i/status/${id}`} />
</blockquote>
</div>
詳細は以下のブログで解説されています。こちらのブログでは<blockquote>
を<div>
で囲うことで解決していますが、dangerouslySetInnerHTML
を使うのがより安全だと考えられます。
twttr.widgets.load()
を呼ぶ必要がある
2. widgets.js
が読み込まれる前に<Tweet>
がレンダリングされる場合は、何もせずとも問題なくツイートが表示されます。一方でwidgets.js
が読み込まれた後に<Tweet>
がレンダリングされる場合は、twttr.widgets.load()
を呼んでツイート内容を取得、描画する必要があります。
上記のコードではuseEffect
において、window.twttr
が存在する場合はtwttr.widgets.load()
を呼ぶようにしています。
詳細はTwitterのドキュメントに記載されています。
Discussion
これって、
Script
をコンポーネント内でなくページにいれた理由ってあるでしょうか?ちょっと気になりました🤔