[Next.js] SNSシェアボタンを実装した

[Next.js] SNSシェアボタンを実装した

やりたいこと

記事の末尾にSNSシェアボタンを置きたい。

X、Facebook、Threads、はてなブックマーク、LINEの5つに対応し、コピーボタンも追加する。

あわせて、このサイトのscheduleページにも同じ機能を入れたい。

主に自分のライブ告知用途で、イベント詳細をそのままXやLINEに貼り付けられるようにしたい。

最近のブラウザにはOSネイティブの共有機能が備わっていて、「わざわざシェアボタンを実装しなくてもいいのでは?」という意見もある。ブラウザの共有ダイアログからSNSに投稿できるし、実際それで十分なケースも多い。

今回実装した理由は単純にこの機能に興味があったからで、そもそもこのブログ自体あまり共有されるような内容でもなく、上記のような必要性の有無は実際のところどうでも良い。

ブログ然とした仕上がりを目指して徐々に機能を追加している最中で、そのひとつの要素にシェアボタン機能があったので実装することにした。

実装の概要

技術スタックはNext.js(App Router)+ TypeScript。シェアボタンはSnsShareButtonsというClient Componentとして切り出した。urlshareTextをpropsで受け取る汎用的な設計にしたことで、記事ページとscheduleページで同じコンポーネントを使い回せている。

// 記事ページ
<SnsShareButtons
  url="https://ofurousagi.com/note/my-post"
  shareText="記事タイトル | ofurousagi"
/>
 
// scheduleページ
<SnsShareButtons
  url="https://ofurousagi.com/schedule/2026-02-14"
  shareText={scheduleShareText}
  copyText={`${scheduleShareText}\n\n${url}\n`}
  exclude={["はてな"]}
/>

exclude にSNS名の配列を渡すと、そのボタンを非表示にできる。scheduleページははてなブックマークを除外している。

アイコンはCSS mask-image を使って実装した。PNG画像のアルファチャンネルをマスクとして使い、background-color: currentColor で色を乗せる。これによってライト/ダークモードの切り替えをCSSだけで吸収できる。

.maskedIcon {
  display: block;
  background-color: currentColor;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

各SNSのシェアURL

それぞれのシェアURLの形式をまとめる。TEXTURL はいずれも encodeURIComponent() でエンコードして渡す。日本語や記号がURLに含まれると壊れるためだ。

const encodedUrl  = encodeURIComponent("https://ofurousagi.com/note/my-post");
const encodedText = encodeURIComponent("記事タイトル | ofurousagi");

X

https://twitter.com/intent/tweet?text=TEXT&url=URL

text にツイート本文、url にシェアするURLを渡す。url パラメータはX側で自動的に短縮されてテキスト末尾に付与される。

ただし url パラメータを使うとテキストとURLの間の改行を制御できない。改行を入れたい場合は url パラメータを使わず、text の中にURLを含めて %0A(改行)で区切る。

https://twitter.com/intent/tweet?text=TEXT%0A%0AURL

Facebook

https://www.facebook.com/sharer/sharer.php?u=URL

URLのみ渡す。表示内容はページのOGPから取得される。

Threads

https://www.threads.net/intent/post?text=TEXT

text に投稿本文を渡す。URLも text に含める。

はてなブックマーク

https://b.hatena.ne.jp/entry/add?url=URL

URLのみ渡す。

LINE

https://line.me/R/share?text=TEXT

text にテキストを渡す。URLも text に含める。

よく紹介される social-plugins.line.me/lineit/share?url= はURLのみ対応の仕様で、本文テキストを追加できない。本文も送りたい場合は line.me/R/share を使う。モバイルではLINEアプリが起動し、PCではLINE for Windows/Macが起動する。

scheduleページのシェアテキスト

scheduleページでは、イベントのフロントマターから以下のフォーマットでシェアテキストを自動生成している。

『イベント名』
 
日付 @会場
 
出演者1
出演者2
 
開場 XX:XX / 開演 XX:XX
 
¥2,500 +1drink ¥500

コピーボタンではこのテキストの末尾にURLも含めた形でクリップボードにコピーされる。Xに貼ってそのまま投稿できる状態を目指した。皆さんもぜひ私のライブを告知してください。

GA4でクリック計測

シェアボタンのクリックはGA4の share イベントで計測している。method パラメータにSNS名(X、LINE など)を渡すことで、どのSNSからシェアされたかを把握できる。

const trackShare = (method: string) => {
  if (typeof gtag !== "undefined") {
    gtag("event", "share", { method, link_url: url });
  }
};

gtag はGA4のスクリプトがグローバルに定義する関数。typeof でチェックしてからコールすることで、スクリプトが未ロードの場合のエラーを防いでいる。

サイト全体のリンククリックは別途 link_click イベントで計測しているため、シェアボタンには data-no-track を付けて二重計測を防いでいる。

<a
  href={href}
  onClick={() => trackShare(label)}
  data-no-track  // グローバルの link_click 計測から除外
>
  {icon}
</a>

余談:CSS知識の退化

今回実装していて気づいたことがある。アイコンの色をCSSで制御したいと思ったとき、mask-image というプロパティ名がすぐに出てこなかった。「PNGのアルファチャンネルを使って色を乗せる方法」というイメージはあるのに、プロパティ名が思い出せない。これは結構ショックだった。

手でCSSを書く機会が減り、AIに任せることが増えた結果だと思う。知識が「なんとなく知っている」状態に薄まっている感覚がある。

AI時代の弊害とも言えるし、役割分担の変化とも言える。ただ、「何ができるかを知っている」ことと「どう書くかを知っている」ことは別物で、前者が失われると適切な指示すら出せなくなる。知識をどう保つか、あるいは保ち方そのものを変えるべきなのか、まだ答えが出ていない。

まとめ

  • url パラメータを使うとXがURLを自動付与するため改行を制御できない。改行を入れたい場合は text にURLを含め %0A で区切る
  • LINEで本文付きシェアをするには social-plugins.line.me/lineit/share ではなく line.me/R/share?text= を使う
  • CSS mask-image + background-color: currentColor の組み合わせでモノクロPNGアイコンをテーマカラーに追従させられる
  • シェアボタンはscheduleページの告知用途にも流用できるよう、urlshareText を外から渡す汎用設計にした

参考リンク

[Giphy API] 自分のアカウントの最新GIFをchangelogに自動表示する