roi-blog

[Next.js]aタグをダウンロードボタンに使うな

お前、Next.js 使ってるよな?で、「ダウンロード機能」作ろうと思って Link コンポーネント使ったら、なんかうまくいかねえってなってるだろ?そうだろ?俺もそうだった。だから、お前がハマった原因と解決法を叩き込んでやるよ。

ダウンロードボタン押しても動かねえ!

お前はこう思ったはずだ。

Link コンポーネントに hrefdownload 属性付ければ普通にいけるだろ」

……それが甘いんだよ。押してもダウンロードされねえ。なんかページ遷移っぽい挙動になるだけで、お前が求めてる「画像のダウンロード」にはならない。は?ってなるよな。

Next.jsのLinkはページ遷移用だって知ってたか?

Link コンポーネントの使い方、公式ドキュメント読んだか?読んでねえだろ?まぁ俺も読んでなかったけどな。

実は Link はクライアントサイドナビゲーション用なんだよ。 簡単に言うと、ページ遷移を高速化するために色々小細工してんの。で、そのせいでネイティブな <a> タグの機能(例えば download 属性)がまともに動かねえんだ。

特に、Next.js 13以降、Link の設計がちょっと変わってて、よりナビゲーション特化になったっぽい。だから「ファイルダウンロード」みたいな使い方するのは完全に間違い。

ネイティブな<a>タグを使えや!

解決法は簡単だ。 Link を諦めて、素直にネイティブな <a> タグ使え。

こう書き換えるだけでOKだ。

<a href={downloadUrl} download="emoji_image.png" className="text-blue-600">
  画像をダウンロード
</a>

これだけで、お前の求めてた「普通のダウンロードボタン」が完成する。

Blob使ってもっと安全にする方法

「いや、<a> タグだけじゃ信用ならねえ!」って奴はこれも試してみろ。 toBlob を使って確実に動くダウンロード機能にしてやるよ。

const handleDownload = () => {
  const canvas = canvasRef.current;
  if (!canvas) return;

  canvas.toBlob((blob) => {
    if (blob) {
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = "emoji_image.png";
      a.click();
      URL.revokeObjectURL(url); // URLを解放
    }
  });
};

そして、こうボタンに組み込め。

<button onClick={handleDownload} className="text-blue-600">
  画像をダウンロードする
</button>

これでブラウザの制限とか関係なく確実にダウンロードができるようになる。

Linkでやろうとするな!

お前がダウンロード機能作ろうとしてハマった原因、それは Next.js の Link コンポーネントが「ページ遷移用」だからだ。 解決法はシンプル、ネイティブな <a> タグを使え。それが嫌なら toBlob を駆使しろ。

次からは同じ失敗すんなよな!

関連記事