CONTENT
ここから
この章でわかること
この章では、ECサイトの基本となる商品一覧ページを作ります。
商品一覧ページは、お客様が商品を探すための入口です。
ここが見づらいと、商品詳細ページまで進んでもらえません。
まずは、次のような流れを作ります。
商品データを用意する
↓
商品型をTypeScriptで定義する
↓
商品カードを作る
↓
商品一覧ページに並べる
↓
価格・画像・売り切れ表示を整える
攻略本でいうと、この章は「最初のショップ画面」を作るステージです。
まだ決済は出てきません。
まずは、商品がきれいに並び、クリックしたくなる状態を作ります。
5.1 商品データの持ち方を決める
ECサイトでは、商品データをどこに持つかを最初に決める必要があります。
商品データとは、次のような情報です。
商品ID
商品名
URL用のスラッグ
説明文
価格
通貨
商品画像
販売中かどうか
カテゴリ
在庫状態
商品データの持ち方には、いくつかの選択肢があります。
静的ファイルに書く
自前DBに保存する
CMSで管理する
Square Catalog APIから取得する
外部の商品管理サービスから取得する
それぞれの特徴は次の通りです。
| 方法 | 向いているケース | 注意点 |
|---|---|---|
| 静的ファイル | 商品数が少ない、学習用、初期開発 | 管理画面から編集できない |
| 自前DB | 本格EC、注文や在庫と連携したい | DB設計が必要 |
| CMS | 商品説明や画像を運用者が編集したい | CMS連携が必要 |
| Square Catalog API | Square POSと商品を共通管理したい | SEO用の長文管理には工夫が必要 |
| 外部商品管理サービス | 大量商品や既存基幹システム連携 | 初期構築が重くなりやすい |
本書では、まず静的ファイルから始めます。
理由は、最初に理解すべきことが「商品をどう表示するか」だからです。
いきなりDBやCMSを入れると、学ぶことが増えすぎます。
まずは、小さく始めます。
最初
↓
静的商品データ
慣れてきたら
↓
DBやCMSへ移行
5.2 静的商品データから始める理由
静的商品データとは、コードの中に商品情報を直接書く方法です。
たとえば、src/data/products.tsのようなファイルを作り、そこに商品一覧を書きます。
src
├─ app
├─ components
├─ data
│ └─ products.ts
└─ types
└─ product.ts
静的データから始めるメリットは、次の通りです。
DB設定が不要
CMS設定が不要
すぐに画面表示を試せる
商品カードのUIに集中できる
型定義を理解しやすい
最初の目的は、完璧な商品管理システムを作ることではありません。
まずは、商品一覧が画面に表示されることです。
攻略本でいうなら、最初の木の剣を手に入れる段階です。
強い装備ではありませんが、最初のステージを進むには十分です。
静的商品データの例
import type { Product } from '@/types/product';
export const products: Product[] = [
{
id: 'product_001',
slug: 'classic-dashi',
name: 'クラシックだし',
description: '毎日の味噌汁や煮物に使いやすい、香り高いだしパックです。',
price: 1200,
currency: 'JPY',
imageUrl: '/images/products/classic-dashi.jpg',
isAvailable: true,
},
{
id: 'product_002',
slug: 'gift-dashi-set',
name: 'ギフトだしセット',
description: '大切な方への贈り物に使いやすい、上品なギフトセットです。',
price: 3500,
currency: 'JPY',
imageUrl: '/images/products/gift-dashi-set.jpg',
isAvailable: false,
},
];
このように、商品データを配列として持ちます。
商品数が少ないうちは、これで十分です。
5.3 商品型をTypeScriptで定義する
商品データを安全に扱うために、TypeScriptで型を定義します。
TypeScript公式のHandbookでは、オブジェクトの形を型として表現できることが説明されています。ECでは、商品や注文のようなデータ構造を型で定義しておくと、存在しないプロパティの利用や値の取り違えを防ぎやすくなります。(typescriptlang.org)
src/types/product.tsを作ります。
/**
* 役割: ECサイト上で表示・販売する商品の基本情報を表す
* 入力: 商品ID、商品名、価格、画像URL、販売状態など
* 出力: 商品一覧・商品詳細・決済処理で利用できる商品データ型
*/
export type Product = {
id: string;
slug: string;
name: string;
description: string;
price: number;
currency: 'JPY';
imageUrl: string;
isAvailable: boolean;
};
それぞれの意味は次の通りです。
| プロパティ | 意味 |
|---|---|
id | 商品を識別するID |
slug | URLに使う文字列 |
name | 商品名 |
description | 商品説明 |
price | 価格 |
currency | 通貨 |
imageUrl | 商品画像のURL |
isAvailable | 販売中かどうか |
なぜ型が必要なのか
型がないと、次のようなミスが起きやすくなります。
priceを文字列で入れてしまう
imageUrlを書き忘れる
isAvailableの名前を間違える
存在しない項目を使ってしまう
型を定義しておけば、エディタやビルド時にミスに気づきやすくなります。
攻略ポイント
商品型は、EC開発の基礎ステータスです。
ここが曖昧だと、後の章で注文、在庫、決済に進んだときに混乱します。
商品型
↓
商品一覧で使う
商品詳細で使う
カートで使う
決済リンク作成で使う
注文データで使う
最初にしっかり作っておく価値があります。
5.4 商品カードコンポーネントを作る
次に、商品カードを作ります。
商品カードとは、商品一覧ページに並ぶ1つひとつの商品表示です。
商品画像
商品名
説明文
価格
販売状態
詳細ページへのリンク
Next.jsでは、ページ間の移動にLinkコンポーネントを使います。公式ドキュメントでは、LinkはHTMLのa要素を拡張し、プリフェッチやクライアントサイドナビゲーションを提供する主要な移動手段として説明されています。(nextjs.org)
src/components/product/ProductCard.tsxを作ります。
import Image from 'next/image';
import Link from 'next/link';
import type { Product } from '@/types/product';
import { formatPrice } from '@/utils/formatPrice';
type ProductCardProps = {
product: Product;
};
/**
* 役割: 商品一覧ページで1つの商品をカード形式で表示する
* 入力: Product型の商品データ
* 出力: 商品画像、商品名、説明、価格、販売状態を含むリンクカード
*/
export function ProductCard({ product }: ProductCardProps) {
return (
<article className="group overflow-hidden rounded-xl border border-gray-200 bg-white">
<Link href={`/products/${product.slug}`} className="block">
<div className="relative aspect-square overflow-hidden bg-gray-100">
<Image
src={product.imageUrl}
alt={product.name}
fill
sizes="(min-width: 1024px) 25vw, (min-width: 768px) 33vw, 100vw"
className="object-cover transition duration-300 group-hover:scale-105"
/>
{!product.isAvailable && (
<div className="absolute inset-0 flex items-center justify-center bg-black/50">
<span className="rounded-full bg-white px-4 py-2 text-sm font-semibold text-gray-900">
売り切れ
</span>
</div>
)}
</div>
<div className="space-y-2 p-4">
<h2 className="text-base font-semibold text-gray-900">
{product.name}
</h2>
<p className="line-clamp-2 text-sm leading-6 text-gray-600">
{product.description}
</p>
<p className="text-sm font-semibold text-gray-900">
{formatPrice(product.price, product.currency)}
</p>
</div>
</Link>
</article>
);
}
商品カードの役割
商品カードは、単なる装飾ではありません。
お客様に次の行動を促すための入口です。
画像を見る
↓
商品名を見る
↓
説明を読む
↓
価格を確認する
↓
詳細ページへ進む
この流れが自然になるように設計します。
攻略ポイント
商品カードに情報を詰め込みすぎないことが大切です。
一覧ページでは、詳しすぎる説明は不要です。
一覧ページ
↓
興味を持ってもらう
詳細ページ
↓
購入を判断してもらう
商品一覧は、あくまで入口です。
5.5 価格表示の共通関数を作る
価格表示は、ECサイト全体で何度も使います。
商品一覧、商品詳細、カート、注文確認、管理画面など、あらゆる場所で価格を表示します。
そのため、共通関数にしておくと便利です。
src/utils/formatPrice.tsを作ります。
type SupportedCurrency = 'JPY';
/**
* 役割: 数値の価格を日本円表記に変換する
* 入力: 価格の数値、通貨コード
* 出力: 画面表示用に整形された価格文字列
*/
export function formatPrice(price: number, currency: SupportedCurrency): string {
return new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency,
}).format(price);
}
使い方は次の通りです。
formatPrice(1200, 'JPY');
表示結果は次のようになります。
¥1,200
なぜ共通関数にするのか
価格表示を各コンポーネントでバラバラに書くと、表示が揃わなくなります。
1,200円
¥1200
¥1,200
1200 JPY
ECサイトでは、価格表示の統一感が大切です。
価格の見せ方がバラバラだと、信頼感が下がります。
攻略ポイント
価格表示は、共通関数で統一します。
商品一覧
商品詳細
カート
注文確認
管理画面
どこでも同じ関数を使うようにします。
5.6 商品画像を最適化する
ECサイトでは、商品画像がとても重要です。
画像が魅力的だと、商品に興味を持ってもらいやすくなります。
一方で、画像が重すぎるとページ表示が遅くなります。
そのため、商品画像は最適化して表示します。
Next.js公式ドキュメントでは、next/imageは画像のサイズ最適化、レイアウトシフト防止、遅延読み込みなどを提供する画像コンポーネントとして説明されています。(nextjs.org)
**Image**コンポーネントを使う
Next.jsでは、通常のimgタグではなく、Imageコンポーネントを使います。
import Image from 'next/image';
<Image
src="/images/products/classic-dashi.jpg"
alt="クラシックだし"
width={800}
height={800}
/>
商品カードでは、正方形の枠いっぱいに画像を表示したいため、fillを使うこともできます。
<div className="relative aspect-square">
<Image
src="/images/products/classic-dashi.jpg"
alt="クラシックだし"
fill
sizes="(min-width: 1024px) 25vw, (min-width: 768px) 33vw, 100vw"
className="object-cover"
/>
</div>
**alt**を必ず書く
altは、画像の代替テキストです。
画像が表示されないときや、スクリーンリーダーで読み上げるときに使われます。
商品画像では、商品名を入れるのが基本です。
alt={product.name}
外部画像を使う場合
外部URLの画像を使う場合は、Next.jsの設定で許可が必要です。
Next.js公式ドキュメントでは、外部画像を扱う場合、next.config.jsまたはnext.config.tsでremotePatternsを設定し、許可する画像元を指定する方法が案内されています。(nextjs.org)
例です。
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
images: {
remotePatterns: [
new URL('https://images.example.com/products/**'),
],
},
};
export default nextConfig;
攻略ポイント
商品画像は、売上に関わる重要装備です。
ただし、重すぎる画像は逆効果です。
よい商品画像
↓
きれい
軽い
比率が揃っている
altが設定されている
悪い商品画像
↓
重い
サイズがバラバラ
説明がない
読み込みが遅い
まずは、商品画像の比率を揃えることから始めます。
5.7 売り切れ表示を作る
ECでは、売り切れ表示が必要です。
売り切れの商品を購入できるようにしてしまうと、後からキャンセル対応が必要になります。
まずは、商品データのisAvailableを使って、販売中かどうかを表示します。
isAvailable: boolean;
商品カードでは、売り切れの場合に画像の上に表示を重ねます。
{!product.isAvailable && (
<div className="absolute inset-0 flex items-center justify-center bg-black/50">
<span className="rounded-full bg-white px-4 py-2 text-sm font-semibold text-gray-900">
売り切れ
</span>
</div>
)}
商品一覧ページでの考え方
商品一覧では、売り切れ商品を完全に非表示にする方法もあります。
ただし、SEOや再入荷予定を考えると、表示したまま「売り切れ」と伝えるほうがよい場合もあります。
売り切れ商品を表示する
↓
再入荷待ち、ブランド認知、SEOに残せる
売り切れ商品を非表示にする
↓
購入できる商品だけ見せられる
どちらが正解かは、商品や運用によります。
最初のおすすめ
最初は、売り切れ商品も表示して、購入ボタンだけ押せないようにするのがおすすめです。
商品一覧では「売り切れ」と表示します。
商品詳細ページでは「現在購入できません」と表示します。
商品一覧
↓
売り切れラベル
商品詳細
↓
購入ボタンを非表示または無効化
攻略ポイント
売り切れ表示は、ユーザーへの約束です。
買えるのか、買えないのかをはっきり伝えます。
曖昧にすると、問い合わせやトラブルが増えます。
5.8 商品一覧のSEO対策
商品一覧ページもSEO対策が必要です。
商品詳細ページほど細かく作り込む必要はありませんが、検索エンジンやSNSに正しく情報を伝える必要があります。
Next.jsでは、Metadata APIを使ってページごとのタイトルや説明文を設定できます。公式ドキュメントでは、静的なmetadataオブジェクトや動的なgenerateMetadata関数を使って、SEOや共有向けのメタデータを定義できると説明されています。(nextjs.org)
商品一覧ページでは、metadataを設定します。
import type { Metadata } from 'next';
import { ProductCard } from '@/components/product/ProductCard';
import { products } from '@/data/products';
export const metadata: Metadata = {
title: '商品一覧|Square Next EC',
description: 'Square APIとNext.jsで作るECサイトの商品一覧ページです。',
};
/**
* 役割: 商品一覧ページを表示する
* 入力: 静的商品データ
* 出力: 商品カードを一覧表示するページ
*/
export default function ProductsPage() {
return (
<main className="mx-auto max-w-6xl px-4 py-12">
<div className="mb-8 space-y-3">
<p className="text-sm font-semibold text-gray-500">Products</p>
<h1 className="text-3xl font-bold tracking-tight text-gray-900">
商品一覧
</h1>
<p className="max-w-2xl text-sm leading-7 text-gray-600">
毎日の暮らしに取り入れやすい商品を揃えました。
気になる商品を選んで、詳細をご覧ください。
</p>
</div>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
</main>
);
}
商品一覧ページのtitle
titleは、ページの内容がわかるようにします。
商品一覧|ブランド名
ギフト商品一覧|ブランド名
だし商品一覧|ブランド名
商品一覧ページのdescription
descriptionでは、どんな商品が並んでいるのかを簡潔に説明します。
どんな商品か
誰に向いているか
何が選べるか
攻略ポイント
商品一覧ページは、ただの一覧ではありません。
検索から来た人に、
ここに探している商品がありそうだと思ってもらう入口です。
h1
↓
ページの内容を明確にする
description
↓
何を選べるページか伝える
商品カード
↓
詳細ページへ進みたくなるようにする
5.9 商品数が増えた場合の設計
最初は、商品数が少ない前提で作ります。
しかし、商品数が増えると、商品一覧ページも設計を変える必要があります。
たとえば、商品が50件、100件、500件と増えた場合、ただ全部を並べるだけでは見づらくなります。
商品数が増えたら、次の機能を検討します。
カテゴリ
検索
並び替え
絞り込み
ページネーション
在庫ありのみ表示
価格帯フィルター
おすすめ順
カテゴリを追加する
商品データにカテゴリを追加します。
export type ProductCategory = 'dashi' | 'gift' | 'limited';
export type Product = {
id: string;
slug: string;
name: string;
description: string;
price: number;
currency: 'JPY';
imageUrl: string;
isAvailable: boolean;
category: ProductCategory;
};
カテゴリがあると、次のようなページを作れます。
/products
/products/dashi
/products/gift
/products/limited
並び順を追加する
商品一覧では、表示順も重要です。
displayOrder: number;
このように表示順を持たせると、運用しやすくなります。
const sortedProducts = [...products].sort(
(firstProduct, secondProduct) =>
firstProduct.displayOrder - secondProduct.displayOrder,
);
DBやCMSへ移行する
商品数が増えたら、静的ファイルだけで管理するのは大変になります。
その場合は、DBやCMSに移行します。
商品数が少ない
↓
静的ファイル
商品数が増える
↓
DB
運用者が編集したい
↓
CMS
実店舗と連携したい
↓
Square Catalog API
攻略ポイント
商品数が少ないうちは、シンプルでよいです。
商品数が増えたら、管理方法を進化させます。
レベル1
静的データ
レベル2
カテゴリ追加
レベル3
並び順追加
レベル4
DB化
レベル5
CMS・Square Catalog API連携
最初からレベル5を目指す必要はありません。
まずは、商品がきれいに並ぶ一覧ページを完成させます。
この章のまとめ
この章では、商品一覧ページを作るための基本を整理しました。
まず、商品データの持ち方を決めました。
最初は静的商品データから始めます。
次に、TypeScriptでProduct型を定義しました。
商品データの形を決めておくことで、後のカート、決済、注文処理にもつなげやすくなります。
商品カードコンポーネントでは、画像、商品名、説明、価格、販売状態を表示しました。
価格表示は共通関数にしました。
商品画像はnext/imageを使って最適化します。
売り切れ商品は、一覧上でわかりやすく表示します。
そして、商品一覧ページにはMetadata APIを使って、SEO向けのtitleとdescriptionを設定しました。
この章で作った流れは、次の通りです。
Product型を定義する
↓
products.tsに商品データを書く
↓
formatPriceで価格表示を整える
↓
ProductCardを作る
↓
商品一覧ページに並べる
↓
SEO設定を追加する
これで、ECサイトの入口となる商品一覧ページが完成します。
次の章でやること
次の章では、商品詳細ページを作ります。
商品一覧ページは、商品を見つけてもらう場所です。
商品詳細ページは、購入を決めてもらう場所です。
次章では、Dynamic Routeを使って商品ごとのページを作り、商品説明、価格、画像、購入ボタン、SEO、構造化データまで整えていきます。