0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EC サイトで「カートに入れる」と「今すぐ購入」を分けるべき理由と実装

0
Last updated at Posted at 2026-05-06

EC サイトで「カートに入れる」と「今すぐ購入」を分けるべき理由と実装

はじめに

EC サイトを作るとき、商品ページに「購入ボタン」を置こうとして、
「カートに追加するのか、直接購入なのか」で迷ったことはありませんか?

これは UX 設計上で明確に分けるべき概念です。
混同すると、ユーザーが「あれ、買ってしまった?カートに入れただけ?」と混乱します。


完成イメージ

実際のアプリでは、作品詳細に「今すぐレンタル」と「カートへ追加」の 2 ボタンを配置しています:

作品詳細CTAボタン

「カートへ追加」を押すとサイドドロワーが開きます:

カートドロワー

「今すぐレンタル」を押すと直接決済画面へ遷移します:

決済画面


2つのボタンが解決する問題

ユーザーの2種類の購買行動

行動A:まとめて購入したい

「この映画とあの映画と、もう1本。3本まとめてレンタルしたい」

→ カートに追加 → カートページで確認 → まとめて決済

行動B:今すぐこれだけ買いたい

「この映画が気に入った。すぐ見たい」

→ 商品ページから直接決済画面へ → 即座に完了

この2つを同じボタンで処理しようとすると、どちらかの UX が犠牲になります。


ボタンの役割の違い

ボタン 役割 画面遷移先
カートに入れる 商品をカートに追加する カートドロワー or カートページ
今すぐ購入 直接決済フローへ進む 決済画面(チェックアウト)

Vue 3 での実装例

<!-- FilmDetailModal.vue -->
<template>
  <div class="film-actions">
    <!-- カートに入れる: カートへ追加してドロワーを開く -->
    <button
      class="btn btn-outline-primary"
      @click="addToCart"
      :disabled="isInCart"
    >
      <i class="bi bi-cart-plus" />
      {{ isInCart ? 'カートに追加済み' : 'カートに入れる' }}
    </button>

    <!-- 今すぐ購入: 直接チェックアウトへ -->
    <button
      class="btn btn-primary"
      @click="buyNow"
    >
      <i class="bi bi-lightning-fill" />
      今すぐ購入
    </button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useCartStore } from '@/stores/cartStore'
import { useRouter } from 'vue-router'

interface Props {
  filmId: number
  title: string
  rentalRate: number
}
const props = defineProps<Props>()
const cartStore = useCartStore()
const router = useRouter()

const isInCart = computed(() =>
  cartStore.items.some(item => item.filmId === props.filmId)
)

// カートに入れる
function addToCart() {
  cartStore.addItem({
    filmId: props.filmId,
    title: props.title,
    rentalRate: props.rentalRate,
  })
  cartStore.openDrawer()  // サイドドロワーを開いてカートを見せる
}

// 今すぐ購入: このアイテムだけでチェックアウトへ
function buyNow() {
  router.push({
    path: '/checkout',
    query: {
      mode: 'buy-now',
      filmId: props.filmId,
    },
  })
}
</script>

チェックアウト画面での「今すぐ購入」モードの処理

/checkout?mode=buy-now&filmId=123 でアクセスされた場合、
カートの内容ではなく、指定された商品だけを決済対象にします。

// CheckoutView.vue
import { useRoute } from 'vue-router'
import { useCartStore } from '@/stores/cartStore'
import { ref, onMounted } from 'vue'

const route = useRoute()
const cartStore = useCartStore()

// 決済対象アイテム
const checkoutItems = ref([])

onMounted(async () => {
  if (route.query.mode === 'buy-now' && route.query.filmId) {
    // 今すぐ購入: このアイテムだけ
    const film = await fetchFilm(Number(route.query.filmId))
    checkoutItems.value = [film]
  } else {
    // 通常フロー: カートの内容
    checkoutItems.value = cartStore.items
  }
})

カートドロワー UI

「カートに入れる」を押したとき、画面を遷移させずにサイドからドロワーが開くのが良い UX です。
ユーザーが「カート確認→買い物続行」か「カート確認→決済へ」を選べるようになります。

<!-- CartDrawer.vue -->
<template>
  <Transition name="slide-right">
    <div v-if="cartStore.isDrawerOpen" class="cart-drawer">
      <div class="cart-drawer-header">
        <h5>カート ({{ cartStore.totalItems }}件)</h5>
        <button @click="cartStore.closeDrawer()"></button>
      </div>
      
      <div class="cart-drawer-body">
        <CartItem
          v-for="item in cartStore.items"
          :key="item.filmId"
          :item="item"
        />
      </div>

      <div class="cart-drawer-footer">
        <div class="total">合計: ¥{{ cartStore.totalAmount }}</div>
        <RouterLink to="/checkout" @click="cartStore.closeDrawer()">
          <button class="btn btn-primary w-100">
            レジに進む
          </button>
        </RouterLink>
      </div>
    </div>
  </Transition>
  
  <!-- オーバーレイ -->
  <div
    v-if="cartStore.isDrawerOpen"
    class="cart-overlay"
    @click="cartStore.closeDrawer()"
  />
</template>

よくある間違い

❌「カートに入れる」ボタンを押したらすぐ決済ページへ遷移させる

カートの概念がない設計になります。
「複数商品をまとめて買いたい」ユーザーが困ります。

❌「今すぐ購入」がカートに入れてから決済ページに遷移させる

カートが汚染されます。
「今すぐ購入」後にカートを開くと、買い終わった商品が残ってしまいます。

❌ ボタンが1つで「カートに入れる or 購入」を切り替えるデザイン

ユーザーが自分の意図を選ぶ手順が増えます。2つのボタンを別々に配置する方が直感的です。


まとめ

カートに入れる 今すぐ購入
目的 複数商品をまとめて購入する準備 1商品をすぐに購入
遷移先 カートドロワー / カートページ 決済画面(チェックアウト)
カートへの影響 カートに追加する カートには追加しない
処理後の行動 引き続き商品を探せる 決済完了まで決済フローに集中

Amazon や楽天が「カートに追加」と「今すぐ買う」を分けているのには、こういった UX 設計の根拠があります。


このアプリでの実装

このDVDレンタルアプリでは、Vue Router や Pinia ではなく、emit イベントと currentPage ref によって実現しています。

作品詳細の CTAボタン(実際のコード)

<!-- FilmDetailView.vue -->
<div class="cta-row">
  <!-- レンタル区分のセクションかどうかで文言を変える -->
  <template v-if="isRentalSection">
    <button class="btn-primary" @click="emit('direct-checkout', film)">今すぐレンタル(決済)</button>
    <button v-if="isAuthenticated" class="btn-secondary" @click="emit('add-to-cart', film)">カートへ追加</button>
  </template>
  <template v-else>
    <button class="btn-primary" @click="emit('direct-checkout', film)">今すぐ購入する(決済)</button>
    <button v-if="isAuthenticated" class="btn-secondary" @click="emit('add-to-cart', film)">カートへ追加</button>
  </template>

  <button class="btn-secondary" @click="emit('add-watch-later', film)">お気に入りに追加</button>
</div>

emit の型定義:

const emit = defineEmits<{
  (e: 'back'): void
  (e: 'direct-checkout', film: PublicFilmSummary): void
  (e: 'add-to-cart', film: PublicFilmSummary): void
  (e: 'add-watch-later', film: PublicFilmSummary): void
}>()

App.vue でのハンドリング(実際のコード)

// App.vue(emit受け取り側)

// カートに入れる:useCart composable 経由でlocalStorageに保存
function handleAddToCart(film: PublicFilmSummary) {
  const added = addToCart(film)
  if (added) {
    cartToastMessage.value = `「${film.title}」をカートに追加しました`
    cartToastTimer = setTimeout(() => { cartToastMessage.value = '' }, 2000)
  }
}

// 今すぐ購入:currentPage を切り替えて決済画面へ遷移
const handleDirectCheckout = (film: PublicFilmSummary) => {
  selectedFilm.value = film
  currentPage.value = 'checkout'
}

カートはルーターやグローバルストアを使わず、useCart composable がシングルトンとして localStorage の永続化を担当。
画面遷移も currentPage という Union Literal 型の ref を書き換えるだけで完結します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?