2026年8月7日

2026年8月7日

WordPressにWeb Share APIでネイティブアプリのような共有ボタンを実装する方法

Web Share API(navigator.share())を使うと、ボタン1つでスマートフォンやパソコンの標準の共有メニューを開き、読者が普段使っているアプリ(LINE、メール、メッセージ、SNSなど)へ記事をそのまま渡せます。WordPressでは、子テーマで記事の下に共有ボタンを出し、対応していないブラウザではリンクのコピーと各SNSの共有リンクに切り替える形が実用的です。この記事では、その実装と、つまずきやすいエラーの扱い、動作確認の方法までを解説します。

Web Share APIでできること・できないこと

navigator.share() に記事のタイトルとURLを渡すと、OSの共有シートが開きます。サイト側でSNSごとのボタンを並べる必要がなく、読者がインストールしているアプリだけが候補に出るのが利点です。外部のスクリプトを読み込まないため、ページが重くならず、共有ボタン経由の追跡もされません。

一方で、次の制約があります。

  • HTTPSのページでしか使えない(開発中の localhost は例外として扱われます)
  • 読者のクリックやタップの中でしか呼べない: ページを開いた瞬間に共有シートを出すことはできません
  • 対応していないブラウザがある: SafariやAndroid版Chromeなどは対応していますが、デスクトップのブラウザでは対応状況にばらつきがあり、Firefoxのデスクトップ版などは未対応です
  • どのアプリに共有されたかは分からない: 共有が完了したかどうかは分かっても、送り先はサイト側に伝わりません

対応状況は更新が続いているため(2026年10月現在)、MDNやCan I useで確認したうえで、未対応の環境向けの代わりの手段を最初から用意することを前提に設計します。

共有ボタンを記事に出す

作業は子テーマの functions.php で行います。テーマを直接編集すると更新で消えるためです。記事のURLとタイトルは data- 属性に入れ、出力時にエスケープします。ネイティブ共有のボタンは最初は隠しておき、対応しているブラウザでだけJavaScriptから表示します。

<?php
// 子テーマの functions.php
add_filter( 'the_content', function ( $content ) {
	if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
		return $content;
	}

	$url   = get_permalink();
	$title = wp_strip_all_tags( get_the_title() );

	$x_url    = add_query_arg( array( 'url' => rawurlencode( $url ), 'text' => rawurlencode( $title ) ), 'https://twitter.com/intent/tweet' );
	$line_url = add_query_arg( 'url', rawurlencode( $url ), 'https://social-plugins.line.me/lineit/share' );
	$fb_url   = add_query_arg( 'u', rawurlencode( $url ), 'https://www.facebook.com/sharer/sharer.php' );

	ob_start();
	?>
	<div class="my-share" data-title="<?php echo esc_attr( $title ); ?>" data-url="<?php echo esc_url( $url ); ?>">
		<button type="button" class="my-share__native" hidden><?php esc_html_e( 'この記事を共有', 'my-child' ); ?></button>
		<div class="my-share__fallback">
			<button type="button" class="my-share__copy"><?php esc_html_e( 'リンクをコピー', 'my-child' ); ?></button>
			<a href="<?php echo esc_url( $x_url ); ?>" target="_blank" rel="noopener">X</a>
			<a href="<?php echo esc_url( $line_url ); ?>" target="_blank" rel="noopener">LINE</a>
			<a href="<?php echo esc_url( $fb_url ); ?>" target="_blank" rel="noopener">Facebook</a>
		</div>
		<p class="my-share__status" aria-live="polite"></p>
	</div>
	<?php
	return $content . ob_get_clean();
} );

add_action( 'wp_enqueue_scripts', function () {
	if ( ! is_singular( 'post' ) ) {
		return;
	}
	$path = get_stylesheet_directory() . '/js/web-share.js';
	wp_enqueue_script(
		'my-web-share',
		get_stylesheet_directory_uri() . '/js/web-share.js',
		array(),
		filemtime( $path ),
		array( 'strategy' => 'defer', 'in_footer' => true )
	);
} );

ここでの判断のポイントは次のとおりです。

  • URLは記事のパーマリンクを使う: アドレスバーのURL(location.href)を使うと、解析用のパラメーターや分割ページの番号まで共有されます。get_permalink() なら記事の先頭のURLに揃います。
  • タイトルからタグを除く: get_the_title() は文字参照やタグを含むことがあるため、wp_strip_all_tags() で整えてから esc_attr() で属性に入れます。
  • スクリプトは記事ページだけで読み込む: is_singular( 'post' ) で絞り、strategy に defer を指定して表示を妨げないようにします。この指定はWordPress 6.3以降で使えます。

各SNSの共有用URLの形式は、サービス側の都合で変わることがあります。動かなくなった場合は、各サービスの開発者向けの案内を確認してください。

JavaScriptで共有シートを開く

// 子テーマの js/web-share.js
(() => {
  document.querySelectorAll('.my-share').forEach((box) => {
    const data = { title: box.dataset.title, url: box.dataset.url };
    const nativeBtn = box.querySelector('.my-share__native');
    const fallback = box.querySelector('.my-share__fallback');
    const status = box.querySelector('.my-share__status');

    const canNative = typeof navigator.share === 'function' &&
      (typeof navigator.canShare !== 'function' || navigator.canShare(data));

    if (canNative) {
      nativeBtn.hidden = false;
      fallback.hidden = true;
    }

    nativeBtn.addEventListener('click', async () => {
      try {
        await navigator.share(data);
        status.textContent = '';
      } catch (err) {
        if (err.name === 'AbortError') return; // 読者が共有シートを閉じただけ
        // 共有できなかったときは代わりの手段を出す
        fallback.hidden = false;
        status.textContent = '共有メニューを開けませんでした。下のボタンをお使いください。';
      }
    });

    box.querySelector('.my-share__copy').addEventListener('click', async () => {
      try {
        await navigator.clipboard.writeText(data.url);
        status.textContent = 'リンクをコピーしました。';
      } catch (err) {
        status.textContent = 'コピーできませんでした。アドレスバーのURLをご利用ください。';
      }
    });
  });
})();

エラーの種類を知っておくと、問い合わせへの対応が楽になります。

  • **AbortError**: 読者が共有シートを閉じたときに発生します。エラーとして表示しないのが正解です。
  • **NotAllowedError**: クリックの外で呼んだ、または iframe の中で許可されていない場合に発生します。埋め込みの中で使うなら、親ページ側の iframe に allow="web-share" が必要です。
  • **TypeError**: 渡したデータが不正なときに発生します。URLが空になっていないか、data-url の出力を確認します。

title・text・url をすべて渡すと、アプリによってはタイトルと本文が重複して表示されます。記事の共有なら title と url だけにしておくと、どのアプリでも読みやすくなります。

画像を添えて共有したい場合

navigator.share() には files でファイルを渡すこともでき、navigator.canShare({ files: [file] }) で事前に対応を確認できます。ただし、ボタンを押してから画像をダウンロードしていると、時間がかかった場合に「読者の操作の中で呼んだ」とみなされず失敗することがあります。画像を添えるなら、ページの読み込み後に File オブジェクトを用意しておき、クリック時には share() を呼ぶだけにします。ファイルの共有に対応する形式や環境は限られるため、対応しない場合はURLだけの共有に戻す処理も入れてください。

見た目とアクセシビリティ

共有ボタンは <button> 要素で作り、キーボードでも操作できるようにします。アイコンだけのボタンにする場合は、画面読み上げ向けの文言を aria-label で付けます。コピーの結果は aria-live の領域に出しているため、画面読み上げでも伝わります。

ボタンを本文の後に出すと、JavaScriptで表示を切り替えても本文の位置は動かず、CLS(レイアウトのずれ)の原因になりにくくなります。共有ボタンをプラグイン(AddToAnyなど)で出している場合は、二重にならないよう、どちらか一方にしてください。

動作を確認する

  1. HTTPSのステージング環境で記事を開き、iPhoneのSafariとAndroidのChromeで「この記事を共有」が表示され、共有シートが開くことを確認します。
  2. 共有先のアプリで、タイトルとURLが正しく入っているか、リンクカード(OGP)が表示されるかを見ます。カードの内容は共有先のアプリがOGPを読みに来て作るため、OGPの設定も合わせて確認します。
  3. 未対応のブラウザ(Firefoxのデスクトップ版など)で、代わりのボタンが表示され、リンクのコピーが動くことを確かめます。
  4. 共有シートを開いてすぐ閉じ、エラーの文言が出ないことを確かめます。

さらに進めて、PWAとしてインストールされたサイトを共有の受け取り先にしたい場合は、マニフェストの share_target を使う方法があります。対応環境が限られるため、まずは送る側の実装を安定させてから検討するのがよいでしょう。

まとめ

  • Web Share APIは、OSの共有シートを開いて読者のアプリへ記事を渡す仕組みで、外部スクリプトが不要です。
  • HTTPSとクリック操作が前提で、未対応のブラウザもあるため、リンクのコピーとSNSの共有リンクを代わりに用意します。
  • URLは get_permalink()、タイトルはタグを除いてエスケープし、data- 属性で渡します。
  • AbortError は読者が閉じただけなのでエラー表示しません。
  • 共有先での見え方はOGPで決まるため、OGPの設定も合わせて確認します。

お気軽にご相談ください

お見積りへ お問い合わせへ
▲