# How to implement a shopping cart in Bubble.io: Step-by-Step Guide

- Tool: Bubble
- Difficulty: Beginner
- Time required: 25-30 min
- Compatibility: All Bubble plans
- Last updated: March 2026

## TL;DR

Build a shopping cart in Bubble using a CartItem data type linked to the User and Product. Users add items via a workflow that creates or updates CartItem records, view their cart in a Repeating Group, adjust quantities, and see real-time price totals. For anonymous users, store cart data in Custom States until they sign up or log in, then migrate items to the database.

## Build a Full Shopping Cart in Bubble

This tutorial walks you through building a complete shopping cart for an e-commerce Bubble app. You will create the data structure, implement add-to-cart workflows, display the cart with quantity controls, calculate totals, and handle both logged-in and guest users. This is a core feature for any online store built on Bubble.

## Before you start

- A Bubble account with an existing app
- A Product data type with name, price, image, and stock fields
- User authentication set up (login/signup)
- Basic familiarity with Repeating Groups and workflows

## Step-by-step guide

### 1. Create the CartItem Data Type

Go to Data tab → Data types → '+ New type' and create 'CartItem' with fields: user (User), product (Product), quantity (number, default 1), and line_total (number). The user field links each cart item to the logged-in user. The line_total will store quantity * product price so you do not recalculate it on every page load. Set Privacy Rules so users can only see their own CartItems (condition: Current User = This CartItem's user).

**Expected result:** A CartItem data type exists with user, product, quantity, and line_total fields, plus privacy rules.

### 2. Build the Add to Cart Workflow

On your product listing or detail page, add an 'Add to Cart' button. Create a workflow: When 'Add to Cart' is clicked → first, search for an existing CartItem where user = Current User AND product = Current cell's Product. If the search count > 0, use 'Make changes to a thing' on the first result to increment quantity by 1 and update line_total. If count = 0, use 'Create a new thing' (CartItem) with user = Current User, product = the product, quantity = 1, and line_total = product's price. Show a confirmation toast or animation.

> Pro tip: Use 'Result of step X' references instead of re-searching for the CartItem. Also, add an 'Only when Current User is logged in' condition and redirect guests to login first, or use the anonymous cart approach covered in step 5.

**Expected result:** Clicking Add to Cart creates a new CartItem or increases quantity if the product is already in the cart.

### 3. Design the Cart Page with Quantity Controls

Create a 'cart' page. Add a Repeating Group with Type of content 'CartItem' and data source 'Do a Search for CartItems (user = Current User)'. In each cell, display: the product image, product name, unit price, a quantity display with minus (-) and plus (+) buttons, the line total, and a Remove (X) button. For the plus button workflow: Make changes to Current cell's CartItem — set quantity to quantity + 1 and line_total to product's price * (quantity + 1). For minus: set quantity to quantity - 1, but add 'Only when quantity > 1'. For remove: Delete Current cell's CartItem.

**Expected result:** A cart page showing all items with working quantity adjustment buttons and a remove option.

### 4. Calculate and Display Cart Totals

Below the Repeating Group, add a summary section. Use dynamic text expressions: Subtotal = 'Do a Search for CartItems (user = Current User):each item's line_total:sum'. Tax = Subtotal * your tax rate (e.g., 0.08 for 8%). Total = Subtotal + Tax. Format all amounts as currency. Add an 'Items in cart' count using 'Do a Search for CartItems (user = Current User):count' — display this as a badge on a cart icon in your navigation bar for visibility across all pages.

> Pro tip: For the nav cart badge, use a reusable element for your header and place the cart count as a dynamic text. It will auto-update across all pages since Bubble searches update in real time.

**Expected result:** Cart page shows subtotal, tax, and total that update automatically when items are added, changed, or removed.

### 5. Handle Guest User Carts with Custom States

For anonymous users who have not logged in, you cannot create database records linked to a User. Instead, use a Custom State on the page (or a reusable header element) of type 'list of CartItems'. When a guest clicks Add to Cart, create the CartItem without a user field and add it to the Custom State list. Display the Custom State list in the cart Repeating Group. When the guest signs up or logs in, create a workflow that iterates through the Custom State list and sets the user field to the newly logged-in user. Then clear the Custom State.

**Expected result:** Guest users can add items to a temporary cart that persists during their session and migrates to their account on login.

### 6. Add a Proceed to Checkout Button

At the bottom of the cart page, add a 'Proceed to Checkout' button. The workflow should validate the cart: check that all products are still in stock (search for each CartItem's product and compare stock_quantity >= CartItem's quantity). If any item is out of stock, show an alert. If all items are available, navigate to the checkout page and pass the cart data. On the checkout page, collect shipping information and trigger the payment flow (Stripe Checkout or your payment provider).

**Expected result:** A checkout button that validates stock availability before proceeding to payment.

## Complete code example

File: `Workflow summary`

```text
DATA TYPES:
- Product
  - name (text)
  - price (number)
  - image (image)
  - stock_quantity (number)
  - description (text)

- CartItem
  - user (User)
  - product (Product)
  - quantity (number, default: 1)
  - line_total (number)

PRIVACY RULES:
- CartItem: user = Current User (Find in searches, View all fields)

PAGE: cart

ELEMENTS:
- RepeatingGroup CartItems
  - Type: CartItem
  - Source: Search for CartItems (user = Current User)
  - Cell layout (Row):
    - Image: Current cell's CartItem's product's image
    - Text: product name
    - Text: unit price :formatted as $#,##0.00
    - Button "-" (decrease qty)
    - Text: quantity
    - Button "+" (increase qty)
    - Text: line_total :formatted as $#,##0.00
    - Button "X" (remove)

- Group CartSummary
  - Text: "Subtotal: " & Search for CartItems:each item's line_total:sum :formatted
  - Text: "Tax (8%): " & (subtotal * 0.08) :formatted
  - Text: "Total: " & (subtotal * 1.08) :formatted (bold)
  - Text: Search for CartItems:count & " items"

- Button "Proceed to Checkout"
- Group EmptyCart (visible when CartItems count = 0)
  - Text: "Your cart is empty"
  - Button: "Continue Shopping" → Go to products page

WORKFLOWS:
1. Add to Cart (on product page)
   → Search for CartItems (user=Current User, product=This Product)
   → If count > 0: Make changes to first result
     quantity = quantity + 1, line_total = product's price * (quantity + 1)
   → If count = 0: Create new CartItem
     user=Current User, product, quantity=1, line_total=product's price

2. Plus button clicked
   → Make changes to Current cell's CartItem
     quantity = quantity + 1
     line_total = product's price * (quantity + 1)

3. Minus button clicked (Only when quantity > 1)
   → Make changes to Current cell's CartItem
     quantity = quantity - 1
     line_total = product's price * (quantity - 1)

4. Remove button clicked
   → Delete Current cell's CartItem

5. Proceed to Checkout clicked
   → Validate stock for all items
   → If valid: Go to checkout page
   → If invalid: Show alert with out-of-stock items
```

## Common mistakes

- **Recalculating line totals on the frontend instead of storing them** — Dynamic calculations in Repeating Group cells run per row and consume workload units. They also do not survive page reloads correctly. Fix: Store line_total as a field on CartItem and update it whenever quantity changes.
- **Not checking stock availability before checkout** — Products may sell out between when a user adds to cart and when they check out, leading to overselling. Fix: Validate stock quantities in the checkout workflow and show clear error messages for out-of-stock items.
- **Allowing quantity to go below 1 with the minus button** — A CartItem with quantity 0 is meaningless and creates confusing display issues. Fix: Add 'Only when Current cell's CartItem's quantity > 1' to the minus button workflow. To remove entirely, use the separate Remove button.

## Best practices

- Store line_total as a database field and update it with each quantity change to avoid repeated calculations.
- Show a cart item count badge in the navigation bar across all pages using a reusable header element.
- Add an empty cart state with a 'Continue Shopping' button for good UX.
- Validate product stock availability at checkout time, not just at add-to-cart time.
- Set Privacy Rules so users can only access their own CartItems.
- Handle edge cases: product price changes after being added to cart, product deletion while in cart.
- For high-traffic stores, clean up abandoned carts with a scheduled backend workflow that deletes CartItems older than 7 days.

## Frequently asked questions

### Should I use a database or Custom States for the cart?

Use the database (CartItem data type) for logged-in users — it persists across sessions and devices. Use Custom States for guest users as a temporary solution, then migrate to database records on login.

### How do I show the cart count in the navigation across all pages?

Create a reusable element for your navigation bar. Add a Text element with the dynamic value 'Do a Search for CartItems (user = Current User):count'. This auto-updates as items are added or removed.

### What happens if a product price changes after it is in the cart?

Since line_total is stored as a field, it reflects the price at add time. Decide on your business logic: either update line totals when prices change (using a database trigger), or lock in the price at add-to-cart time.

### How do I handle cart items for products that get deleted?

When deleting a product, first delete all associated CartItems, or soft-delete the product (mark as inactive). Add conditional formatting to show 'This product is no longer available' in the cart.

### Can I add product variants (size, color) to the cart?

Yes. Create a ProductVariant data type linked to Product with fields for size, color, SKU, and price. Link CartItem to ProductVariant instead of (or in addition to) Product.

### Is building a complete e-commerce checkout in Bubble complex?

A basic cart is straightforward, but full e-commerce (variants, shipping, tax rules, inventory management, order processing) gets complex. Teams like RapidDev specialize in building production-ready e-commerce systems in Bubble.

---

Source: https://www.rapidevelopers.com/bubble-tutorial/implement-a-shopping-cart-in-bubble
© RapidDev — https://www.rapidevelopers.com/bubble-tutorial/implement-a-shopping-cart-in-bubble
