
コピーボタンを押したとき、画面が何も変わらなかったらどう感じますか。
おそらく「あれ、コピーできたのかな」と思って、もう一度押します。 それでも何も変わらなければ、「できてるはずだけど…」と半信半疑のまま貼り付けてみる。 そういう経験が、誰にでもあると思います。
このブログにコピーボタンを実装するとき、 押したあとに「コピーしました!」と2秒だけ表示する処理を追加しました。 実装自体は数行で終わります。 ボタンを押したときのフィードバックは大事ですよ、今回はそんな話です。
コンピューターは知っているけど、画面には映らない
コピーの処理自体は一瞬で完了します。 ボタンを押した瞬間、クリップボードへの書き込みはもう終わっています。
でも、その事実はどこにも表示されません。 ボタンの見た目は変わらない。ページは動かない。 何かが起きたはずなのに、起きていないように見える。
これが「フィードバックがない」状態の正体です。 処理は完了しているのに、ユーザーにはその完了が伝わっていない。
目に見える変化のある操作なら問題ありません。 フォームを送信すればページが遷移する。 ファイルを削除すれば一覧から消える。 変化そのものが「完了した」という証拠になります。
コピーはそうではありません。 クリップボードの中身は画面に映らない。 だから何かを伝える必要があります。
「2秒」という設計
「コピーしました!」を永続的に表示し続けるのも違和感があります。 コピーはあくまで一時的な操作なので、その事実を伝えたらすぐ元に戻るのが自然です。
実装はこういう形にしました。
tsxconst handleCopy = async () => { await navigator.clipboard.writeText(url) setCopied(true) setTimeout(() => setCopied(false), 2000) }
setCopied(true) でボタンの表示を「コピーしました!」に切り替えて、2秒後に setCopied(false) で元に戻す。それだけです。
2秒という時間に深い根拠があるわけではありません。 1秒だと少し慌ただしく感じるし、3秒だとやや長い。 感覚的に「これくらいで十分伝わる」と思った長さです。
画面を見ていない人のことを考えた
コピーボタンを押す人は、ボタンを押した直後に画面から目を離すことが多いと思います。 コピーしたらすぐ別のところに貼り付けるために、エディタやチャットに視線を移す。
そう考えると、フィードバックの役割は「確認させる」ことよりも「安心させる」ことに近いかもしれません。 見ていなくてもいい。でも見ていれば確認できる。 その程度のさりげなさが、使いやすさにつながるのだと思いました。
コピーボタン以外にも当てはまること
この話はコピーボタンに限りません。
「いいね」を押したとき。フォームを送信したとき。ファイルをアップロードしたとき。 ユーザーが何かをして、その結果が画面に現れないとき、必ず同じ問いが生まれます。 「これ、ちゃんとできたのかな」という不安です。
開発者の視点から見ると、処理が完了しているかどうかはコードを読めばわかります。 でもユーザーにはコードが見えない。 見えないものに対しては、何か別の方法で「伝える」必要があります。
今回の「コピーしました!」は、その伝え方のひとつです。 2秒で消えるメッセージにすぎないけれど、それがあるかないかで、使っている人の体験はずいぶん変わると思っています。





