Next.js アプリケーションで静的にツイートを埋め込む 「static-tweet」 を使いやすくカスタマイズしてみた

Next.js アプリケーション内で Twitter 標準の埋め込みツイートを使用してみたら激重だったので、静的な HTML としてツイートを埋め込む 「Static Tweet Next.js Demo」 のソースコードを参考に、自分が使いやすいようにカスタマイズしたバージョンを作ってみたというお話

つい先日ですが、著書の特設サイト (下記リンク) を作っているとき、コンテンツの一部として、Twitter でつぶやいた書籍に関連するツイートを埋め込もうと思ったんですね。

ちなみに、こういう簡単なランディングページっぽいものは、Next.js 使っています。よく使うような UI コンポーネントも自前で用意してあるので、簡単なページを短期間でササッと作りたい時にはとても便利。

で、話をツイートの件に戻しますが、今まで Next.js アプリケーションにツイートを埋め込むっていう要件を扱ったことがなかったのでどうしようかなと考えた結果、最初は普通に Twitter 標準の埋め込みツイート機能を使用して終わりでいいやって思っていたんですけども、それで実装してみたあとで、念のためにパフォーマンス計測している時、埋め込みツイート、めちゃくちゃ重てぇってことに気がつきまして・・・・・・ Lighthouse のスコアで Performance 項目が赤く染まったのをみてダメだこりゃってなったわけです。

そこで、そういえば以前、Next.js で静的にツイートを埋め込むデモを見たぞ、ということでメモっておいた 「Static Tweet Next.js Demo」 を確認。ソースコードを拝見して、使わせて頂くことにしました。

元のソースコードだと複数のツイートを読み込む際にちょっと使いにくかったので、その辺を含めて再利用がしやすいように改変しましたが、今回はそれについてまとめてみます。

Next.js アプリケーションでツイートを埋め込む

最も単純にツイートを埋め込むなら、Twitter が標準で提供している 「埋め込みツイート」 の機能を使用すればよいということになります。

公式の埋め込みツイート機能で取得できるソースコードには、ツイートごとに <script> タグが付いてきますが、これは分離し、_app.js などで、下記のように読み込んでやればよいので、パフォーマンスとかそういうのをとりあえず置いておいて単に使用するだけならそれ程難しいことではありません。

useEffect(() => {
  const tweet = document.createElement('script');
  tweet.setAttribute('src', 'https://platform.twitter.com/widgets.js');
  tweet.setAttribute('defer', 'true');
  document.head.appendChild(tweet);
}, []);

標準の埋め込みツイート クソ重い問題

これでとりあえず実装してみて気がついたんですけども、冒頭に書いたとおりすげぇ重たい。

で、ちょっと調べてみたら、埋め込みツイート内 (Twitter から読み込まれた JavaScript によって、blockquote で記述されたツイートのソースコードは、iframe 要素に変換されるんですけども、その iframe 要素内) で、2MB 以上ある JavaScript ファイルが読み込まれてて、これが著しくパフォーマンスを落としていることがわかりました。

embed.vendors~ondemand.Tweet.e1fe983255cfe9803951.js みたいなファイル名の .js ファイルなんですが、ファイルサイズがでかい上に、埋め込まれたツイートの各 iframe 要素内で読み込まれるので、簡単にいえばこのサイズのファイルが、埋め込んだツイートの数だけ読み込まれると。そりゃ重たいわ。

埋め込みツイート内で読み込まれた .js ファイルの例

さすがにこりゃないわ、ってことで、標準の埋め込みツイートは使用せず、静的にツイートを埋め込む方法で実装しなおすことにしました。

Static Tweet to your next app

まず、今回のソースコードは下記にまとめています。

適当にツイートを埋め込んだデモも下記に用意しておきましたので、あわせてどうぞ。

冒頭にも書いたとおり、ベースとしたのは下記で、Next.js における SSG のデモとして公開されていたソースコードです。

デモ自体は getStaticPropsgetStaticPaths 関数、および getStaticPaths における fallback の挙動を紹介するためのものですので、Next.js アプリケーション内に複数のツイートを埋め込むような用途を想定したコードにはなっていません。

できれば、

<Tweet id="[tweet_id]" />

みたいな感じで簡単にツイートを埋め込みたかったので、頑張ってコード書こうって手を付けたんですけども、static-tweetreadme.md を読んだら、ご親切に最後に 「おめーの Next.js アプリケーションに静的にツイートを埋め込みたい場合はこっちのソースコード参考にしろよ(意訳)」 ってサンプルソースへのリンクが書かれてて、もうこれ、私がやりたかったことそのままじゃんってことでほぼ丸パクリの勢いで参考にさせて頂きました。

オリジナルからの変更点

基本的にベースにさせて頂いたソースコードから必要な部分だけを抜き出しているので、私が書いたオリジナルの要素はほぼないんですけども、いくつか私が使いやすいように改変しました。大したことやってないので、下記に箇条書きにしてまとめておきます。

  • 元のソースコードは、パッケージを最新の状態にしたときにツイートが正常に埋め込まれないっていう現象にぶち当たりました。調べてみたところ remark-parse のアップデートにより、パーサーが変更された(参考リンク)ことで、Markdown のパースに失敗しているようでしたので、それに対応するため、remark-gfm パッケージの導入と、それに合わせたソースコードの改変を行っています。
  • static-tweet/components/post/tweet.js 内で、isDark 変数をセットすることで、埋め込まれるツイートのデザインを、「ライトテーマ」、または 「ダークテーマ」 から選択可能にしました。
  • システムのテーマカラーでダークテーマを選択している場合は、prefers-color-scheme メディアクエリを使用して、自動的に埋め込みツイートのデザインがダークテーマになるようにしてあります。
  • その他、気になったところをちょこちょこと細かく修正

実際の使用方法は、pages/index.js のソースコードを見ていただければすぐわかるレベルだと思います。

Twitter API を経由してデータを取得、静的な HTML としてアプリケーション内に埋め込まれるため、非常に軽いですし、特に複数のツイートを Next.js アプリケーション内に埋め込む場合はパフォーマンスの面で大きく向上させることができると思います。

ちなみに、元のツイートが削除された場合は、ビルド時にデータ取得が失敗することになりますが、逆にいえば再ビルドしない限りは表示され続けることになります (静的に HTML として埋め込まれているので当然ですが)。

この辺、自分のツイートを埋め込む場合は気にする必要ないと思いますが、他人のツイートを埋め込む用途に使用した場合、ツイート主の意思に反して埋め込みツイートが表示され続けることになりますので、気をつけた方がよいと思います。

静的に埋め込んだツイートの表示例

Social Share