By AI Language Model (Gemini 2.5 Pro)

ダウンロードボックスコンポーネントの使い方

ダウンロードボックスコンポーネントの実装概要と使い方

本稿では、Tailwind CSSを用いて作成した再利用可能なダウンロードUIコンポーネント(DownloadBox.jsx)の概要と、MDXでの使い方を解説する。

output_gray.png
ダウンロード

実装概要

  • ファイル名(fileName)とファイルURL(fileUrl)をpropsとして受け取る。
  • ファイルサイズは自動で取得され、MB単位で表示される。
  • descriptionは任意で指定可能であり、省略した場合は表示されない。
  • Tailwind CSSでデザインされており、どのMDXファイルでも容易に再利用が可能である。
  • PDFや画像など、public/フォルダに配置した任意のファイルに対応する。

使い方

  1. MDXファイルの先頭でインポートする

    import DownloadBox from '../../components/DownloadBox.jsx'
  2. コンポーネントを呼び出す

    最小構成(ファイル名とURLのみ)
    <DownloadBox
      fileName="sample.pdf"
      fileUrl="/files/sample.pdf"
      client:visible
    />
    説明文を追加する場合
    <DownloadBox
      fileName="sample.pdf"
      fileUrl="/files/sample.pdf"
      description="サンプルファイルのダウンロード"
      client:visible
    />

注意点

  • 対象ファイル(例: PDF)はpublic/フォルダに配置する必要がある。
  • fileUrlには/ファイル名のようにルートパスで指定する。
  • descriptionは省略可能である。
  • MDXで使う場合は必ずclient:visibleを付与する必要がある。

以上の手順により、どのMDX記事でも簡単にダウンロードボックスを表示できる。

付録(コンポーネントのソースコード)


import { useEffect, useState } from 'react';

function formatFileSize(size) {
  if (!size) return '';
  const num = Number(size);
  const units = ['B', 'KB', 'MB', 'GB', 'TB'];
  let idx = 0;
  let value = num;
  while (value >= 1024 && idx < units.length - 1) {
    value = value / 1024;
    idx++;
  }
  const display = value % 1 === 0 ? value.toString() : value.toFixed(2).replace(/\.00$/, '').replace(/(\.[1-9])0$/, '$1');
  return `${display} ${units[idx]}`;
}

export default function DownloadBox({ fileName, fileUrl, description = '' }) {
  const [fileSize, setFileSize] = useState('');

  useEffect(() => {
    async function fetchFileSize() {
      try {
        const res = await fetch(fileUrl, { method: 'HEAD' });
        const size = res.headers.get('content-length');
        setFileSize(formatFileSize(size));
      } catch (e) {
        setFileSize('');
      }
    }
    fetchFileSize();
  }, [fileUrl]);

  return (
    <div className="border border-gray-800 rounded-md p-4 flex items-center gap-4 bg-gray-800 mb-4">
      <span className="text-2xl text-gray-400">
        {/* クリップアイコンSVG */}
        <svg className="w-6 h-6" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l7.07-7.07a4 4 0 00-5.657-5.657l-7.071 7.07a6 6 0 108.485 8.486l6.364-6.364" />
        </svg>
      </span>
      <div className="flex-1 min-w-0">
        <div className="font-semibold text-gray-200">{fileName}</div>
        {fileSize && <div className="text-xs text-gray-400">{fileSize}</div>}
        {description && <div className="text-xs text-gray-500">{description}</div>}
      </div>
      <a
        href={fileUrl}
        download
        className="inline-flex items-center px-4 py-2 bg-[#6366f1] text-white rounded hover:bg-[#4f46e5] hover:text-white transition"
      >
        {/* ダウンロードアイコンSVG */}
        <svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" strokeWidth="2" viewBox="0 0 24 24">
          <path strokeLinecap="round" strokeLinejoin="round" d="M4 16v2a2 2 0 002 2h12a2 2 0 002-2v-2M7 10l5 5m0 0l5-5m-5 5V4" />
        </svg>
        ダウンロード
      </a>
    </div>
  );
}