この記事では Web Share API と URLコピー機能、そして X(Twitter)シェア を組み合わせた共有機能の実装方法を紹介します。
Next.js と TypeScript を使用しつつ、ブラウザサポート状況を考慮したフォールバックを実装していきます。
対応する機能
- Web Share API(対応ブラウザのみ)
- URLコピー(すべてのブラウザ)
- X(Twitter)シェア(リンクベース)
完成イメージ
- Web Share API に対応しているブラウザの場合は、ネイティブの共有ダイアログが表示される
- どのブラウザでも URL コピーが必ずできる
- アイコンなどで視覚的にわかりやすいボタンを配置
- X(Twitter)へのシェアボタンも用意し、記事タイトルとURLを含むツイート画面を別タブで開く
実装コード
ShareButtonコンポーネント
'use client'
import { useState, useEffect } from "react"
interface ShareButtonProps {
url: string
title: string
}
export default function ShareButton({ url, title }: ShareButtonProps) {
const [canShare, setCanShare] = useState(false)
const [showCopiedMessage, setShowCopiedMessage] = useState(false)
useEffect(() => {
// ブラウザがNavigator.shareをサポートしているかを判定
setCanShare(typeof navigator !== "undefined" && "share" in navigator)
}, [])
// === Web Share API対応の場合 ===
const handleShare = async () => {
try {
await navigator.share({
title,
text: title,
url,
})
// シェア成功・またはユーザーキャンセルでも特に何もしない
} catch {
// シェア失敗時は静かにキャッチ(ユーザーキャンセル等も含む)
}
}
// === URLコピー機能 ===
const handleCopyUrl = async () => {
try {
// まずClipboard APIが使えるか試す
await navigator.clipboard.writeText(url)
setShowCopiedMessage(true)
setTimeout(() => setShowCopiedMessage(false), 2000)
} catch {
// Clipboard APIが使えない場合のフォールバック(execCommand)
const textArea = document.createElement("textarea")
textArea.value = url
document.body.appendChild(textArea)
textArea.select()
try {
document.execCommand("copy")
} catch {
// さらに失敗した場合も静かにキャッチ
}
document.body.removeChild(textArea)
setShowCopiedMessage(true)
setTimeout(() => setShowCopiedMessage(false), 2000)
}
}
// === X(Twitter) シェア ===
// シェア時にはタイトル・URLを組み込み、別タブで開く
const handleShareOnTwitter = () => {
const shareUrl = `https://twitter.com/intent/tweet?text=${encodeURIComponent(title)}&url=${encodeURIComponent(url)}`
window.open(shareUrl, "_blank", "noopener,noreferrer")
}
return (
<div className="flex gap-4">
{/* Copy URL Button - Always visible */}
<button
onClick={handleCopyUrl}
className="group inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors relative"
aria-label="Copy URL to clipboard"
>
{/* ここにリンクアイコン等を置く */}
{showCopiedMessage && (
<span className="absolute -top-8 left-1/2 -translate-x-1/2 bg-background border px-2 py-1 rounded text-sm whitespace-nowrap">
URLをコピーしました
</span>
)}
</button>
{/* Native Share Button - Only on supported browsers */}
{canShare && (
<button
onClick={handleShare}
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
aria-label="Share article"
>
{/* ここに共有アイコン等を置く */}
</button>
)}
{/* X(Twitter) Share Button */}
<button
onClick={handleShareOnTwitter}
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
aria-label="Share on Twitter"
>
{/* ここにTwitterのアイコン等を置く */}
</button>
</div>
)
}
実装のポイント
-
クライアントサイド実装の明示
-
use client
ディレクティブを先頭につけることで、サーバーサイドレンダリング時に navigator が参照されるのを防ぎ、ハイドレーションエラーを回避 - useEffect 内でブラウザAPIの存在判定を行う
-
-
シンプルなエラーハンドリング
- navigator.share のエラーはユーザーのキャンセルも含むため、シンプルに静かにキャッチ
- URLコピーでも、クリップボードAPIが使えない時は execCommand("copy") にフォールバック
-
URLコピーの多段フォールバック
- まずは新しい navigator.clipboard を試し、失敗時は execCommand("copy") へ
- コピー完了後は短いトーストメッセージを表示し、ユーザに成功を伝える
-
アクセシビリティの考慮
- ボタンには aria-label を設定して用途を明示
- アイコンは aria-hidden="true" や role="img" を状況に合わせて付与するとよい
-
SSRとの相性
- Next.js 13以降では、サーバーコンポーネントとクライアントコンポーネントの切り分けが重要
- 本記事のようなブラウザ機能を使う場合は、クライアントコンポーネント ('use client') が必須
カスタマイズのポイント
- 見た目のカスタマイズ
Tailwind CSSを例にすると、クラスで柔軟に変更可能です。
className="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors"
- フィードバックのカスタマイズ
コピー完了時に表示するメッセージや、表示時間を変更することも簡単です。
setShowCopiedMessage(true)
setTimeout(() => setShowCopiedMessage(false), 2000)
動作確認
Web Share API 対応ブラウザ
- iOS Safari (iOS13+)
- Chrome for Android
- Edge for Android など
URLコピー&Xシェア
- すべてのモダンブラウザで動作
- クリップボードAPIが非対応の場合でも execCommand("copy") が発動
まとめ
- Web Share API によるネイティブUIのメリットを活かしつつ、未対応ブラウザでは URLコピー をフォールバックとして提供
- X(Twitter)シェア もリンクベースで簡単に実装できる
- Next.js (App Router) と TypeScript の組み合わせでも特に難しい設定は不要
これにより、ユーザーがどのブラウザを使用していても、シェアに関する操作をスムーズに行えるようになります。ぜひプロジェクトで活用してみてください。
参考文献
- Dev.toスタイルのブログ記事レイアウトを導入してみた | tech.jugoya.ai
- Web Share API - MDN Web Docs
- Navigator.share() - MDN Web Docs
- Can I use Web Share API?
もしご不明点や追加してほしい項目があれば、ぜひコメントをお寄せください!