はじめに
「画像最適化」は、UX・速度・運用コストの三軸で確実に効果が出る領域です。しかし現場では、 “作った画像をそのままアップロードする” 運用が続き、画質劣化/転送量肥大/デバイスごとの表示崩れが慢性的に発生しています。
この記事では、Figmaから本番配信までを 完全自動化する“解像度別エクスポート戦略” と、React/Next.jsに共通で使える 運用に強い画像コンポーネント を提示します。
結論として、以下の4要素を揃えると運用は一気に安定します。
(1)解像度は1x/2x/3x
(2)フォーマットはAVIF/WebP/JPEG
(3)pictureによる最適フォールバック
(4)CI/CDでsharp自動生成
1. Figmaからは「3解像度 × PNG」だけ作ればよい
現場で最も事故が多いのが “Figmaエクスポートを手作業で最適化する” というプロセスです。
整理すべきルールをひとつだけ:これだけで運用が80%安定します。
Figma → PNG → 1x / 2x / 3x の3種類のみ出す
| 解像度 | 用途 | 例 |
|---|---|---|
| 1x | 標準DPI・縮小配置 | hero.jpg |
| 2x | Retina標準 | hero@2x.jpg |
| 3x | 高解像度端末(Pixel, iPad Pro等) | hero@3x.jpg |
※ SVGはアイコン専用。ブラーやシャドウがある画像は必ずラスタに。
Figmaからの画像エクスポートは、プラグインで一括して行えます。
2. React/Next.js 共通で使える「ResponsiveImage」コンポーネント
解像度/フォーマットの自動選択を <picture> に任せ、
開発者は “srcを拡張子なしで指定するだけ” の状態にするのがベストです。
// utils/image-loader.ts
interface ResponsiveImageProps {
src: string; // 拡張子なしで与える: '/images/hero'
alt: string;
sizes?: string;
loading?: 'lazy' | 'eager';
priority?: boolean;
}
export const ResponsiveImage: React.FC<ResponsiveImageProps> = ({
src,
alt,
sizes = '100vw',
loading = 'lazy',
priority = false
}) => {
const base = src.replace(/\.[^/.]+$/, '');
return (
<picture>
{/* 最軽量:AVIF */}
<source
type="image/avif"
srcSet={`
${base}.avif 1x,
${base}@2x.avif 2x,
${base}@3x.avif 3x
`}
sizes={sizes}
/>
{/* モダンブラウザ:WebP */}
<source
type="image/webp"
srcSet={`
${base}.webp 1x,
${base}@2x.webp 2x,
${base}@3x.webp 3x
`}
sizes={sizes}
/>
{/* フォールバック:JPEG/PNG */}
<img
src={`${base}@2x.jpg`}
srcSet={`
${base}.jpg 1x,
${base}@2x.jpg 2x,
${base}@3x.jpg 3x
`}
alt={alt}
loading={loading}
sizes={sizes}
fetchPriority={priority ? 'high' : 'auto'}
/>
</picture>
);
};
この実装で以下を自動的に達成できます:
- 端末に応じて 1x/2x/3x を最適選択
- ブラウザ能力に応じて AVIF → WebP → JPEG の順に自動選択
- LCP改善のための
priority設定も可能
3. アイコン管理は SVG + Spritesheet が最適解
アイコン管理を複雑にしているのは「色のバリエーション」と「重複ファイル」です。
整理するルールとしては:
単色アイコンは全部 SVG
→ ビルドで sprite 化
→<Icon name="search" />で使用
これだけで色変更・テーマ変更・差分レビューのすべてが軽くなります。
4. sharp による自動最適化パイプライン(CI/CD)
PNG→JPEG/WebP/AVIF 変換は手作業ではなく、CI/CDで自動化するのが正解 です。
以下は実務で使える Node.js スクリプト例です。
// scripts/build-images.js
// 役割:./assets/raw 以下の元画像から、1x/2x/3x の JPEG/WebP/AVIF を自動生成し ./public に出力するスクリプト
import sharp from 'sharp';
import fs from 'fs';
import path from 'path';
// 元画像が格納されているディレクトリを読み込み
for (const file of fs.readdirSync('./assets/raw')) {
// 拡張子を除いたベース名(例: hero.png → hero)
const base = file.replace(/\.[^.]+$/, '');
// 入力ファイルの絶対パス
const input = path.join('./assets/raw', file);
// 1x / 2x / 3x の3解像度分を生成
for (const scale of [1, 2, 3]) {
// ファイル名の接尾辞(1xは無印、2xは @2x、3xは @3x)
const suffix = scale === 1 ? '' : `@${scale}x`;
// ---- JPEG 出力 ----
// - 目安として幅 600px を基準に、scale倍する
// - quality は 82 前後が画質とサイズのバランスが良い
sharp(input)
.resize({ width: 600 * scale })
.jpeg({ quality: 82 })
.toFile(`./public/${base}${suffix}.jpg`);
// ---- WebP 出力 ----
// - JPEG よりも高圧縮・高画質を狙う
// - 一部古いブラウザでは未対応だが、多くのモダンブラウザで有効
sharp(input)
.resize({ width: 600 * scale })
.webp({ quality: 82 })
.toFile(`./public/${base}${suffix}.webp`);
// ---- AVIF 出力 ----
// - 現時点で最も高圧縮なフォーマット
// - quality 値は WebP/JPEG より低め(50程度)でも十分高画質
sharp(input)
.resize({ width: 600 * scale })
.avif({ quality: 50 })
.toFile(`./public/${base}${suffix}.avif`);
}
}
ポイント
- CIで生成することで「画像を直接触る運用」が消失
- WebP/AVIF は常に自動生成されるため抜け漏れゼロ
- キャッシュ用の
immutableパス付与も容易
5. ディレクトリは“原本 → 自動生成 → 公開”の3構成
/assets/raw ← FigmaのPNG原本
/public/images ← 自動生成されたJPEG/WebP/AVIF
/src/components ← ResponsiveImage
/src/utils
まとめ:画像最適化は「仕組み化」した安定させる
この記事で紹介した戦略に沿うと、実務上の課題はほぼ消えます。
- 画質の劣化が消える
- 端末別の表示崩れが消える
- 手作業の圧縮や命名事故が消える
- CDNキャッシュが安定する
- LCPが確実に改善する
特に、Figma→PNG→CI/CDで自動最適化 → ResponsiveImage という流れは
“人が介在しない” ため、長期運用に最も強い構造です。
本記事が、あなたのプロジェクトの画像戦略を「再現性の高い仕組み」へ転換するきっかけになれば幸いです。