旧ブログ(ISSEN)から移行しました

【React】コピペOK! QRコードダウンロードボタンの実装

【React】コピペOK! QRコードダウンロードボタンの実装

杉田侑祐
杉田侑祐8分で読めます

はじめに

この記事の概要

こんにちは、株式会社TOKOSの杉田です!
今回はReactを用いたQRコードのダウンロードボタンを作成します!
QRコード生成用のライブラリの比較もしましたので、最後まで見ていただけると幸いです!

この記事では、以下の内容について詳しく解説します:

  • QRコード生成用のライブラリ
  • Blobデータの変換

対象読者

  • Reactを用いたフロントエンド開発を行っている方
  • QRコード生成ライブラリを選定している方

この記事で扱う内容、扱わない内容

この記事で扱う内容 :

  • QRコード生成ライブラリについて
  • Blobデータの生成・変換方法

この記事で扱わない内容:

  • JavaScriptの基本的な使用方法
  • Tailwindcssについて

今回の成果物

今回は非表示状態のQRコードをダウンロードできるボタンを作成いたします!

開発環境

  • next: 15.1.4
  • react: ^19.0.0
  • tailwindcss: ^4.0.1

QRコード生成ライブラリの比較(2025年 3月)

まずはQRコード生成ライブラリの選定を行います。

今回は2つのライブラリを比較します。
比較するライブラリは下記になります。

  1. react-qr-code
  2. qrcode.react

比較項目は下記になります。

  • Weekly Download数
  • 最終更新日
  • パッケージサイズ(Minified)

Weekly Download数

react-qr-codeqrcode.react
373,4771,459,090

最終更新日

react-qr-codeqrcode.react
9ヶ月前3ヶ月前

パッケージサイズ(Minified)

react-qr-codeqrcode.react
16.8kB16.4kB

比較結果として3つの項目全てでqrcode.reactに軍配があがりました!
今回はqrcode.reactを使用しようと思います!

https://www.npmjs.com/package/qrcode.react

www.npmjs.com

実装

今回はPNGデータでダウンロードすることを目標とします。

QRCodeSVGの呼び出し

今回はQRコードを非表示にし、ダウンロードのみできる実装としたいのでhidden(display:none)を使用しています!
またQRCodeはSVGとしています!

React
"use client"
 
import { QRCodeSVG } from "qrcode.react"
 
export function QrCodeDownloadButton({ profileIdPromise, clientRoleType }: Props) {
  const url = "https://to-ko-s.com"
 
  return (
    <>
      <button>QRコードダウンロード</button>
      {/* display:noneで隠しています */}
      <div className="hidden">
        <QRCodeSVG value={url} size={512} level="H" />
      </div>
    </>
  )
}

QRCodeSVGに渡しているPropsは下記になります。

  1. value={url}
    • QRコードに埋め込むデータ(URL)を指定します
  2. size={512}
    • QRコードのサイズをピクセル単位で指定します。この場合、512×512ピクセルのQRコードが生成されます。
  3. level="H"
    • QRコードのエラー訂正レベルを指定します。"H"はもっとも高いエラー訂正レベルで、QRコードの約30%が損傷しても読み取り可能になります!
      他のレベルには"L"(低)、"M"(中)、"Q"(四分位)があります。

またSVGとしている理由は変換の柔軟性を優先しているためです!

QRCodeSVGの呼び出し後にボタンを押せるようにする

まずはDOMにアクセスするためにuseRefを使用します!
さらにDOMへのアクセスが可能になるまではダウンロードボタンを押せないようにuseEffect + useStateを用いて管理します!
useStateではBooleanを扱いbutton要素のdisabled属性にBooleanの値を追記します。

React
"use client"
 
import { QRCodeSVG } from "qrcode.react"
 
export function QrCodeDownloadButton({ profileIdPromise, clientRoleType }: Props) {
  const [isReady, setIsReady] = useState(false)
  const qrRef = useRef<HTMLDivElement>(null)
 
  useEffect(() => {
    if (qrRef.current?.querySelector("svg")) {
      setIsReady(true)
    }
  }, [])
 
  const url = "https://to-ko-s.com"
 
  return (
    <>
      <button disabled={!isReady}>QRコードダウンロード</button>
      {/* display:noneで隠しています */}
      <div ref={qrRef} className="hidden">
        <QRCodeSVG value={url} size={512} level="H" />
      </div>
    </>
  )
}

これでQRコードのレンダリング後にダウンロードボタンを押せるようになりました!

ダウンロード機能の実装

ここからダウンロード機能の実装に入ります!

流れは下記になります!

  1. useRefでアクセスする
  2. Canvas要素の作成
  3. img要素を作成
  4. 作成したimg要素に取得したSVGの情報を付与する
  5. CanvasにSVG画像を描画させダウンロードする

ではコードの紹介です。

React
"use client"
 
import { QRCodeSVG } from "qrcode.react"
 
export function QrCodeDownloadButton({ profileIdPromise, clientRoleType }: Props) {
  const [isReady, setIsReady] = useState(false)
  const qrRef = useRef<HTMLDivElement>(null)
 
  useEffect(() => {
    if (qrRef.current?.querySelector("svg")) {
      setIsReady(true)
    }
  }, [])
 
  const url = "https://to-ko-s.com"
 
  const downloadQrCode = () => {
    // 1.useRefでアクセス
    const svg = qrRef.current?.querySelector("svg")
    if (!svg) return
 
    // 2.Canvas要素の作成 + Canvas要素のサイズ指定
    const canvas = document.createElement("canvas")
    const ctx = canvas.getContext("2d")
    if (!ctx) return
    // display:noneの要素ではclientWidth/clientHeightが0になるため、QRCodeSVGのsizeと同じ値を直接指定
    const size = 512
    const pixelRatio = window.devicePixelRatio || 1
    canvas.width = size * pixelRatio
    canvas.height = size * pixelRatio
 
    // 3.img要素を作成
    const img = new Image()
 
    // 4.作成したimg要素に取得したSVGの情報を付与する
    const svgData = new XMLSerializer().serializeToString(svg)
    img.src = `data:image/svg+xml;base64,${btoa(svgData)}`
 
    // 5.CanvasにSVG画像を描画させダウンロードする
    img.onload = () => {
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
      //pngデータを指定、jpegにしたい場合はimage/jpegになる
      const pngUrl = canvas.toDataURL("image/png")
      const link = document.createElement("a")
      link.href = pngUrl
      link.download = "qr-code.png"
      link.click()
    }
  }
 
  return (
    <>
      <button onClick={downloadQrCode} disabled={!isReady}>
        QRコードダウンロード
      </button>
      {/* display:noneで隠しています */}
      <div ref={qrRef} className="hidden">
        <QRCodeSVG value={url} size={512} level="H" />
      </div>
    </>
  )
}

別途、手順4の解説

  • new XMLSerializer().serializeToString(svg)について

    • 取得したSVGデータをXML文字列に変換しています。
    • new XMLSerializer()でインスタンスの作成、.serializeToString(svg)メソッドを呼び出し、SVG要素をXML形式の文字列に変換しています!
  • img.src = data:image/svg+xml;base64,${btoa(svgData)}

    • データURL(data:スキーム)は、ファイルの内容を直接URLに埋め込む方法になります!
      通常のURLがリソースの「場所」を指定するのに対し、データURLはリソースの「内容」自体を含みます。
    • btoa()はJavaScriptの組み込み関数で、文字列をBase64形式にエンコードします。Base64エンコードは、バイナリデータやテキストデータを、ASCII文字のみを使用して表現するための方法になります!
    • data:image/svg+xml;base64,${btoa(svgData)}の処理
      • btoa(svgData)で、SVGのXML文字列をBase64エンコード
      • data:image/svg+xml;base64,${エンコードされたデータ}という形式のデータURLを作成
        • data
          • データURLであることを示すプロトコル識別
        • image/svg+xml
          • MIMEタイプ(コンテンツの種類がSVG画像であることを示す)
        • base64
          • エンコード方式がBase64であることを示す
        • ,
          • ヘッダーとデータを区切る区切り文字
        • ${btoa(svgData)}
          • Base64エンコードされたSVGデータ

一見何してんだ?となりますが、分解すれば理解できると思います!

最終的なコード

React
"use client"
 
import { QRCodeSVG } from "qrcode.react"
 
export function QrCodeDownloadButton({ profileIdPromise, clientRoleType }: Props) {
  // ~~~~ 省略(useRef, useState, useEffect)~~~~
  const url = "https://to-ko-s.com"
 
  const downloadQrCode = () => {
    // 1.useRefでアクセス
    const svg = qrRef.current?.querySelector("svg")
    if (!svg) return
 
    // 2.Canvas要素の作成 + Canvas要素のサイズ指定
    const canvas = document.createElement("canvas")
    const ctx = canvas.getContext("2d")
    if (!ctx) return
    // display:noneの要素ではclientWidth/clientHeightが0になるため、QRCodeSVGのsizeと同じ値を直接指定
    const size = 512
    const pixelRatio = window.devicePixelRatio || 1
    canvas.width = size * pixelRatio
    canvas.height = size * pixelRatio
 
    // 3.img要素を作成
    const img = new Image()
 
    // 4.作成したimg要素に取得したSVGの情報を付与する
    const svgData = new XMLSerializer().serializeToString(svg)
    img.src = `data:image/svg+xml;base64,${btoa(svgData)}`
 
    // 5.CanvasにSVG画像を描画させダウンロードする
    img.onload = () => {
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
      //pngデータを指定、jpgにしたい場合はimage/jpgになる
      const pngUrl = canvas.toDataURL("image/png")
      const link = document.createElement("a")
      link.href = pngUrl
      //qr-code.pngとしてダウンロード
      link.download = "qr-code.png"
      link.click()
    }
  }
 
  return (
    <>
      <button onClick={downloadQrCode} disabled={!isReady}>
        QRコードダウンロード
      </button>
      {/* display:noneで隠しています */}
      <div ref={qrRef} className="hidden">
        <QRCodeSVG value={url} size={512} level="H" />
      </div>
    </>
  )
}

さいごに

今回はReactでQRコードのダウンロードボタンを作成しました!
url部分をPropsで受け取れば汎用的なコンポーネントになるかと思います!
Blobデータを扱う際は、理解していないと何をしているかわからなくなるため、Blobデータとは何かを学ぶいいきっかけになりました!

この記事を書いた人

杉田侑祐
杉田侑祐

TOKOSのフロントエンドエンジニア兼UI/UXデザイナー。このブログではフロントエンドメインで投稿しています。HIPHOPとゲームが好きです✌️