• Vue 3
  • Composition API
  • Axios
  • REST API
  • Tailwind CSS
  • CodePen

Pokémon shopping cart with Vue 3, Axios and the PokéAPI

How a Vue 3 shopping cart demo consumes the PokéAPI REST API: batched lazy loading with Promise.all, client-side pagination, a reactive cart with computed totals, loading and error states, and a simulated checkout.

Cristopher Paniagua By 6 min read

What does the demo do?

This is a shopping cart demo that uses Pokémon as products. It's built with Vue 3, the Composition API, Axios and Tailwind CSS, and it gets its catalog from the public PokéAPI REST API.

Features:

  • A product catalog fetched dynamically from a REST API.
  • Lazy loading and client-side pagination: products load in batches of 10, up to 100.
  • A reactive cart that stays in sync with the product list.
  • Quantity management: add, increase, decrease and remove items.
  • Computed totals for item count and price.
  • Loading and error states for every request.
  • A responsive layout and a simulated checkout flow.
The live demo on CodePen. Switch to the JS tab to read the full source.

How does it fetch products from the PokéAPI?

The PokéAPI list endpoint (/api/v2/pokemon?limit=10&offset=0) only returns each Pokémon's name and URL. The image, types and stats need a separate request per Pokémon.

Making those requests one after another would be slow, so the app runs them in parallel with Promise.all. A whole batch then resolves in about the time of its slowest request:

import axios from 'axios'

const BATCH_SIZE = 10
const MAX_PRODUCTS = 100

const fetchBatch = async (offset) => {
  const { data } = await axios.get('https://pokeapi.co/api/v2/pokemon', {
    params: { limit: BATCH_SIZE, offset },
  })

  return Promise.all(
    data.results.map(({ url }) => axios.get(url).then(res => res.data))
  )
}

Simplified for this article. See the CodePen for the exact implementation.

How do lazy loading and pagination work?

The app doesn't request all 100 products up front. It loads 10 at a time and fetches the next batch as the user pages through the catalog. This keeps the first load fast and avoids sending traffic to a free public API for data nobody looks at.

import { ref, computed } from 'vue'

const products = ref([])
const loading = ref(false)
const error = ref(null)

const canLoadMore = computed(() => products.value.length < MAX_PRODUCTS)

const loadMore = async () => {
  if (loading.value || !canLoadMore.value) return
  loading.value = true
  error.value = null

  try {
    const batch = await fetchBatch(products.value.length)
    products.value.push(...batch.map(toProduct))
  } catch (e) {
    error.value = 'Could not load products. Please try again.'
  } finally {
    loading.value = false
  }
}

The loading guard stops a second request for the same batch from starting while one is still in flight. Using finally means the loading state is always cleared, even when a request fails.

How is the cart state managed?

The raw API responses are transformed into a small product shape that the UI needs (id, name, image, price). The cart is a reactive list of products with quantities. Totals are computed, never stored, so they can't fall out of sync:

const cart = ref([])

const addToCart = (product) => {
  const item = cart.value.find(item => item.id === product.id)
  if (item) item.quantity++
  else cart.value.push({ ...product, quantity: 1 })
}

const totalItems = computed(() =>
  cart.value.reduce((sum, item) => sum + item.quantity, 0)
)

const totalPrice = computed(() =>
  cart.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)

Because the catalog and the cart share the same product id, the product list and the cart stay in sync.

What happens at checkout?

The checkout is simulated. It completes the shopping flow from browsing to ordering without a real payment provider, and no payment data is collected.

What frontend concepts does it demonstrate?

  • REST API integration with Axios and async/await.
  • Performance-conscious API use: batching, parallel requests with Promise.all, lazy loading and a hard limit.
  • Data transformation from API responses into a UI-friendly model.
  • Reactive state management with ref and computed.
  • Resilient UX with loading and error states.
  • Responsive UI with Tailwind CSS.

Frequently asked questions

Which technologies does the Pokémon shopping cart use?

Vue 3 with the Composition API, Axios for HTTP requests, Tailwind CSS for the responsive UI, and the public PokéAPI REST API as the product data source.

How are the Pokémon loaded?

The app loads Pokémon lazily in batches of 10, up to a maximum of 100. For each batch it fetches the list of Pokémon and then requests every Pokémon's details in parallel with Promise.all.

Why use Promise.all to fetch Pokémon details?

The PokéAPI list endpoint only returns names and URLs, so each Pokémon's details need their own request. Promise.all sends those requests in parallel, so a batch takes about as long as its slowest request instead of the sum of all of them.

How are cart totals calculated?

Cart items are reactive state, and the total quantity and total price are computed properties derived from them, so they update automatically when items are added, removed or change quantity.

Does the checkout process real payments?

No. The checkout is simulated to show the full shopping flow. No payment data is collected or sent anywhere.

Cristopher Paniagua

Written by

Cristopher Paniagua

Senior Software Engineer specializing in frontend development with Vue, Nuxt, React and TypeScript. Author of open source packages like vue3-autocounter and speaker at Vue.js meetups.

Reading this with an AI tool? Use the markdown version of this article or the site's llms.txt.

logo © 2026 Cristopher PS