Tech Blog

Why You Should Separate 'Add to Cart' and 'Buy Now' in an EC Site, and How to Implement It

by Tech Writer
Vue3 UX TypeScript Frontend

In EC sites, there are two different buttons: “Add to Cart” and “Buy Now.”

These two buttons serve different purchasing behaviors. Understanding the difference — and implementing accordingly — is the foundation for improving conversion rates and user experience.

Two Types of Purchasing Behavior

Users who visit an EC site don’t all buy in the same way. Some think “I’ll buy this later together with something else,” while others think “I want this now, immediately.”

Add to Cart: For users who want to continue browsing, compare prices, or add multiple items before paying.

Buy Now: For users who have already decided, who want to minimize the steps to payment.

If only one button exists, the burden on certain users increases.

The Difference in Button Roles

Add to CartBuy Now
BehaviorAdd item to cart → Open cart drawerSkip cart → Go directly to checkout
Cart stateReflects in cartDoesn’t affect cart
Target userComparison shoppersUsers who’ve decided
UXCheck button → Confirm in drawerQuick jump to payment

It’s important to note that “Buy Now” doesn’t touch the cart.

This means: pressing “Buy Now” doesn’t add to cart, doesn’t clear the cart, and the cart’s contents don’t change after the Buy Now flow either.

Implementation Example with Vue 3

Here is a simplified implementation example using Vue 3.

FilmDetailModal.vue (Product Detail Modal)

Two buttons are placed: “Add to Cart” and “Buy Now.”

<template>
  <div class="modal">
    <h2>{{ film.title }}</h2>
    <p>{{ film.description }}</p>

    <div class="actions">
      <button @click="addToCart">Add to Cart</button>
      <button @click="buyNow">Buy Now</button>
    </div>
  </div>
</template>

<script setup lang="ts">
const props = defineProps<{ film: Film }>();
const emit = defineEmits<{
  (e: "add-to-cart", film: Film): void;
  (e: "buy-now", film: Film): void;
}>();

function addToCart() {
  emit("add-to-cart", props.film);
}

function buyNow() {
  emit("buy-now", props.film);
}
</script>

Importantly, this component only emits events — it doesn’t directly manipulate the cart state.

App.vue (Event Handling)

App.vue receives events from the modal and handles them differently.

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

const cartStore = useCartStore();
const router = useRouter();

function handleAddToCart(film: Film) {
  cartStore.addItem(film);
  isCartDrawerOpen.value = true; // Open cart drawer
}

function handleBuyNow(film: Film) {
  // Store Buy Now item separately — don't touch cart
  buyNowItem.value = film;
  router.push({ name: "checkout", query: { mode: "buy-now" } });
}
</script>

For “Add to Cart,” the cart state is updated and the drawer is opened. For “Buy Now,” separate state is used and navigation goes directly to checkout.

CheckoutView.vue (Buy Now Mode Processing)

Checkout side-checks the route query and switches the display accordingly.

<script setup lang="ts">
import { useRoute } from "vue-router";
import { useCartStore } from "@/stores/cart";

const route = useRoute();
const cartStore = useCartStore();
const props = defineProps<{ buyNowItem?: Film }>();

// Determine what to purchase based on mode
const isBuyNowMode = computed(() => route.query.mode === "buy-now");

const itemsToCheckout = computed(() => {
  if (isBuyNowMode.value && props.buyNowItem) {
    return [props.buyNowItem]; // Only the Buy Now item
  }
  return cartStore.items; // Normal cart contents
});
</script>

This ensures the cart contents aren’t affected at all during Buy Now flow.

CartDrawer.vue

The cart drawer is an independent component that simply displays the cart contents. Since “Buy Now” doesn’t touch the cart, the drawer contents aren’t affected.

<template>
  <div class="cart-drawer" :class="{ open: isOpen }">
    <h3>Cart</h3>
    <ul>
      <li v-for="item in cartStore.items" :key="item.id">
        {{ item.title }} × {{ item.quantity }}
      </li>
    </ul>
    <button @click="router.push('/checkout')">Proceed to Checkout</button>
  </div>
</template>

Common Mistakes

Mistake 1: Buy Now Also Adds to Cart

If “Buy Now” also calls cartStore.addItem(), user trust breaks down. The feeling that “I pressed Buy Now but a different item remained in the cart” leads to cart abandonment.

Mistake 2: Buy Now Clears the Cart

If pressing “Buy Now” clears the cart, a user thinking “I want to buy this one now, but also check out the other items later” loses their carefully arranged cart.

The correct approach is to manage Buy Now state separately from the cart.

Mistake 3: Only One Button Provided

From a UX perspective, if only “Add to Cart” exists, users who have decided are forced to go through the cart. This increases unnecessary steps and can reduce conversion.

Importance of UX Design Decisions

This “separation of two buttons” actually involves decisions that go beyond UI design:

  • What state to manage for Buy Now items
  • Whether Buy Now state persists across page navigation
  • Whether to show confirmation screen in Buy Now flow

These are decisions where design, state management, and routing all intersect. In this project’s implementation, the decisions made are recorded in the UX Decision Log.

Actual Screens

CTA buttons on the film detail page (“Rent Now” and “Add to Cart”):

Film detail CTA buttons

Cart drawer after pressing “Add to Cart”:

Cart drawer

Checkout screen after pressing “Rent Now”:

Checkout screen


Summary

“Add to Cart” and “Buy Now” serve completely different user intents. The key points are:

  • Buy Now doesn’t touch the cart
  • Components only emit events, state is managed in the parent
  • Checkout switches display based on mode

Properly separating these two functions leads to checkout flow that users can use comfortably.

Feel free to send a message

Please send a message if you have any technical questions, feedback, or inquiries.