Repeater - add to cart - calculate the final price


#1

Hi everyone! :sunny:

I’ve a problem that i haven’t solved yet…hope someone with more experience than me can help! :slight_smile:
I’ve a classic e-commerce…with 2 repeaters, one for items that you can buy, the other with the items you put in the cart.
I’m able to update rows, send the correct quantity of items to the repeater cart for each individual product, and the same for the price of each item ( multiply price of item * number of the product ).

MY BIG ISSUE IS :frowning: : how can i bring each “final price” of each item into the repeater cart and put all of its in one variable ? ( so i can calculate the total price and the total price with VAT)

Hope someone will help me.
Have a good day everyone :ok_hand:


#2

it might be best if you can upload your .rp file here. That is usually the easiest and fastest way for others here to help you.

It sounds like you can do this already, but I’ll touch on details of this later on…

In general, there are several basic methods to calculate a tally of repeater row values, depending on how you structure your repeaters and handle user interactions (adding/changing/choosing items) as well as if both repeaters are on the same page or different pages. You can store totals in global variable(s) or the text value of widget(s) either inside or outside the repeater–but if you need to refer to these values on another page then you must use global variables.

The widgets which show these totals should be outside both repeaters.

Here are some basic methods to keep track of totals. Each has its own advantages and disadvantages.

  1. Update a running total “on the fly” as a direct result of user interactions in the “product catalog” repeater–typically via the Click or Tap event of a widget in the repeater, like a button, or Text Changed event of a Text Field. As a very simple example, let’s say you have a repeater with two columns in its data sheet: “Product” and “Price”, and in the repeater cell, a widget for each of these, along with an “Add To Cart” button.
  • You can create a global variable to store the running Cart total, let’s say it is named, “VarCartTotal”. (Its default value is zero, which you can set either in the Page Loaded event or the repeater’s Loaded event.)
  • The “Add To Cart” widget has a Click or Tap which would include a Set Variable Value action like, "Set Variable Value of VarCartTotal to [[VarCartTotal + Item.Price]]" (along with action to add or update a row in the cart repeater.)
  • If you have a widget showing the number of items and/or a repeater column to store this number, you can have a set of conditional cases to handle a change to 1 (e.g. first time “Add To Cart” is clicked) --to add a row to the cart; and greater than 1–to update an existing row in the cart; and less than 1 (or “equal to 0”) --to delete a row in the cart repeater. For example,
    If text on MyProductCount equals "1" Add Row to CartRepeater
    Else if text on MyProductCount is greater than "1" Update Row of CartRepeater
    …(with rule like “[[TargetItem.Product == Item.Product]]”
    Else if text on MyProductCount is less than "1" Delete Row of CartRepeater
    …with same rule to match rows.
  • This method is pretty straightforward, but it will fail if you alter the catalog repeater in any way, such as sorting, filtering, adding or removing rows, etc. because each of these actions would trigger the repeater’s Item Loaded event, thus resetting everything to default, thus losing any user selections.

  1. Or you can use the Item Loaded event with conditional cases. This lets the repeater automatically calculate totals in a self-contained way. This requires a column per each value you need to track–like the number of items per product. Instead of just directly setting the text value or global variable value when an item is added/edited, you update the row in the same repeater (as well as adding/updating a row in the cart repeater.) This method takes a little more setup time and forethought, but is more reliable and flexible as it supports sorting, filtering and other catalog changes.
  • The “Add To Cart” button would have an action like,
    Update This Row …where you select the column to update, like “Count” with expression like, [[Item.Count + 1]]
  • The repeater’s Item Loaded would have a set of conditional cases like,
    If [[Item.isFirst]] equals "true" Set Variable Value of VarCartTotal equal to "0"
    If true Set Text (of your repeater widgets)
    Set Variable Value of VarCartTotal equal to [[VarCartTotal + (Item.Count * Item.Price)]]
    • Thus, every time the item repeater is updated it runs through each row, first initializing the running total (VarCartTotal global variable) to zero, then adding the cost of each item (multiplied by the selected number per item)
    • You could also build in the logic to update the cart repeater, here in the Item Loaded event rather than the “Add To Cart” button (and whatever else controls you might have like ‘+’ and ‘-’ buttons, text entry, a “Remove” button, etc.)

  1. Or, you can calculate the “final price” per item and the totals (price, VAT, price with VAT) all in the cart repeater, based purely on the items in the cart. This method works if both repeaters are on the same page. If the cart repeater is on a different page, the product repeater (or a “mini cart” repeater on first page) would first need to set some global variables for the totals.
  • The catalog repeater adds/updates/removes rows to the cart repeater as described above. But, it is not concerned with any totals. This approach can help keep the functions and logic separated, which should be more reliable, easier to debug, and more reusable. So basically, the function of the catalog repeater is just to show products (along with names, prices, images, descriptions etc.) and allow them to be added to the cart–that’s it. The function of the cart repeater is to show selected products and calculate totals (as well as change counts of products, remove them, etc. as needed)
  • The cart repeater would have columns for (at least) Product, Price, Count.
  • The cart repeater’s Item Loaded event would contain the conditional cases to initialize the total value(s), and update these totals for each row (using the same logic as shown in method 2 above.)

  1. If you have a catalog repeater on one page (or multiple pages) and a cart repeater on its own separate page, you could also set up one global variable per possible product, or use one global variable to store an array of selected products, their prices and item counts. Now, Axure does not support true arrays (unfortunately) but you can kind of manually create one as a “hash string” and then on the cart page you can parse this string to construct the cart repeater and its running totals. This is a fairly advanced method, so I’ll just point to a forum thread which demonstrates it. This method works if you have many pages that need to refer to “cart details” --such as multiple catalog/product pages, and/or separate “checkout” pages that might need to access, edit and/or recreate a cart.

Good luck, and if you have any specific questions or would like to see some examples of these methods, reply here and I’ll post some examples.


#3

Hi mbc66,

First of all, really thank you for your reply! I’ve really appreciated :pray:
I have tried experimenting with the second and third methods you explained, but sadly something is still not working for me…
If you would like to post some example, i would really love it! meanwhile, i attach my file, so you can see it if you want…
Unfortunately I have only been using axure for a few months, and I still struggle to understand some concepts.
I hope to hear from you soon!


https://x0sqih.axshare.com/#id=zednrf&p=sandwhich_page___correct_-local_var


#4

Well, this is quite an ambitious prototype for only using Axure a few months. You’ve done well so far! Repeaters are advanced–some of the most complicated things you can do in Axure, and take a lot of time and experience to get used to using–and even then, usually require a bit of trial and error to get them working properly.

Here is an updated file (too large to post on this forum) with a summary of what I changed. I basically use the third method as outlined above.

In the ++Sandwiches repeater:

  • I changed the unused C_TotalPrice column to C_inCart, with possible values of “true” and (not true: so “false” or blank). This helps handle button clicks when a row needs to be added, updated, or deleted in the cart repeater.

  • I updated the Set Text so the price value includes a ‘$’ --in general it is best to keep the repeater datasheet numeric values as pure numbers and then format as needed when setting the text. This makes math with them much easier.

  • I changed the Click or Tap events for the (-) , (+) and Cart buttons.

    • First of all, you don’t need to set text of a repeater widget prior to updating the row, because the text already gets set in the Item Loaded event.

    • I removed all the Set Variable Value actions, so the Cart repeater can handle these in a more reliable way.

    • It has been awhile since I’ve done e-commerce stuff, but I recall usability issues when there is both an “add to cart” button and buttons to change the quantity. Things like, how do you handle a cart button press when quantity is “0”? (…and looks like you chose to add the item to cart, but the quantity in the product repeater doesn’t get updated.) Similar for decreasing amount to “0” --users expect it would remove the item from the cart without having to press the cart button or go to the cart. …etc.

      • I chose to automatically update quantity to “1” if user presses cart button first.
      • Likewise, if an item is in the cart and the quantity is decreased to “0” that item gets removed.
      • Basically, all this just means having a few conditional cases on these buttons’ interaction code.
      • Obviously, you can adjust the interaction model as you need.
    • The cart button has 3 cases:

      • If Item not yet in cart and quantity is “0”

        • Add a row to the cart repeater (with item quantity of “1”).
        • Then update “this” row in product repeater to show quantity of “1”, and set the C_inCart column to “true”. This must be done after updating the cart repeater, because as soon as it gets called, everything in this repeater–including this button–gets reset. So, if the order of these two actions were reversed, the second–Add Row to cart repeater–would never get called.
      • Else if item is not in the cart (and thus quantity greater than 0)

        • Add row to cart repeater, using the expression,
          [[Item.C_Price * Item.C_quantity]]
        • Then update “this” row to set the C_inCart column to “true”.
      • Else if item is in the cart already, just update the “Price$” column in the cart repeater.

        • (It turns out this case is not needed if the (-) and (+) buttons update the cart quantities directly. I kept it just in case.)
        • I use this expression in the Rule field to ensure the correct row in the cart gets updated:
          [[Item.C_title == TargetItem.Name$]]
          …where “Item.” refers to “this” repeater and “TargetItem.” refers to the "targeted’ repeater–in this case, the cart.
    • The RemoveQuantity button has three similar cases:

      • If item is in cart already and quantity is greater than 1, update rows
      • Else if item is in cart and quantity is 1 (thus now reduced to 0), remove the row from cart
        • …Except I discovered the totals don’t get properly updated if the row is simply removed. So, first I update the row in the cart to set the quantity to “0”, which triggers proper calculation of totals (in the cart), then Wait 10 ms (to ensure it has been updated) then Delete Row from cart repeater, then Update Row in the product repeater.
      • Else if item is not in cart yet, just update this row to decrease the quantity.
        (This would only happen if user pressed (+) more than once, then pressed (-), all before ever pressing the cart button.)
    • The AddQuantity button has two cases:

      • If item is in cart, update rows in both repeaters
      • Else if not in cart, only update this row’s quantity
  • In the ++ShoppingCart_modal I added widgets for “Subtotal” and “VAT (15%)”, above your “Total”. All three of these values get calculated in the Item Loaded event of the cart repeater.

  • In the ++ShoppinigCart_Repeater I added a few cases to Item Loaded to calculate totals:

    • Case 1, initialize count
      If [[Item.isFirst]] equals "true"
      Set Variable Value
      NumbersOfOrdersVar to "0"
      TotalpriceVar to "0"
    • Case 2, (always)
      If true
      (set text, set image)
      Set Variable Value
      NumbersOfOrdersVar to [[NumbersOfOrdersVar + Item.Quantity$]]
      TotalpriceVar to [[TotalpriceVar + Item.Price$]]
    • Case 3, show the count and totals
      If [[Item.isLast]] equals "true"
      Wait 10 ms (this should ensure it happens last, after variables updated in case 2
      Set Text (of the subtotals and cart count)
      • The “.toFixed(2)” is a built-in function to set “two decimal precision” so the monetary values are always shown with two decimal places for the cents.


#5

First of all, really really thank you for your time and work!
it’s very complicated for me, but i’m trying to learn and understand the logic behind that.
I apologize for the response time, but i was very busy.
I would like to ask you a few things to better understand what u’ve done :sunny:
1)can you explain me the logic of item.isFirst and item.isLast on the item loaded event on the cart repeater? I’m also studying javascript for that, but unfortunately i’m still a noob and i really would like to understand.
2) so you can calculate the total price just by add it’s variable to the item of the repeater, and then set the text ?
3) why you’ve multiplied the totalPriceVar * .15 ?

I hope you will still be very patient with me, thank you again for your time!
I congratulate you for your skills :muscle:


#6

Yes, this is a way to identify the first row and the last row in a repeater, for the purpose of calculating a total based on the value of a column in the repeater. So, when the repeater is loaded, and every time it is “touched” --a row is updated, added, deleted, or if the repeater is sorted or filtered-- its Item Loaded event gets fired, which basically rebuilds the repeater. This Item Loaded event happens successively for every row (Item) in the repeater. To properly calculate a total value based on column data, it is necessary to first set the “total value” variable (i.e., TotalPriceVar) to zero (know as “initializing” a variable), then successively add the column data for each row to this variable, and then (if needed) do something with the total after all the rows have been loaded and all their data has been added to the total value.

“Item” is a pointer to the current repeater row. So, if a repeater has 3 rows, there are three unique “Items”. Each Item has many properties, such as the columns in the repeater datasheet. To access these properties, a “dot” (the period character: ‘.’ ) is used to connect the Item to a specific property, for example, [[Item.Column1]]. So, .isFirst and .isLast are two built-in properties of a repeater Item. It is a way to determine if the row is currently the first Item, and if the row is currently the last item in the repeater. Both of these are Boolean values, meaning they can only have one of two possible values: true or false. It may seem a bit odd to have to test if the first row of a repeater is really the first row, but realize the repeater can be sorted and/or filtered in many ways, thus changing the order of the rows as well as the availability of rows (in the case of filters.)

There are a lot of properties you can access for repeaters, as well as other widgets and “system data” like dates, time, browser window, cursor, etc. Click the “Insert Variable or Function…” link in the “Edit Text” dialog to browse and select them. Here is the Axure documentation for this:

…And yes, these are all using (at least based on) javascript methods, so it can be very useful to have an understanding of how javascript works. I’ve found that just googling “javascript” and whatever I want to do usually leads to a solution I can use in Axure, as well as explanation and examples for properties and functions I find in Axure. For example, “javascript get tomorrow’s date” or “javascript isFirst”. Furthermore, the answers posted on the Stack Overflow site are usually good and reliable.

Yes you can calculate the total price by:

  1. Initializing a variable to zero. (This ensures you start counting at 0.)
  • (This is why it is important to determine the first row of a repeater)
  • This variable can be a global variable.
  • This variable can also be the text value of any widget. i think of this as a “page-local variable”.
  • (There are advantages and disadvantages to using global variables and widget text values. Which you choose just depends on the context and your personal preference)
  1. Adding the value from a row’s column, once per row, for every available row, to the variable.

  2. Determining when the last row of a repeater has been added to this total, thus ensuring you have the complete “total” value.

  • Using the Item Loaded event with a conditional case of If [[Item.isLast]] equals "true" is a reliable way to know when all the available rows of a repeater have been loaded–thus all the data values have been added to your total value variable. In other words, to know when Item Loaded is done loading all the repeater rows. That is when you can do something with the total value, such as setting text in your cart.

Your original question asked how you can calculate the total price and the total price with VAT. I just used “.15” as an example, where the VAT is 15%. (Of course, you can use whatever percentage you need for this “VAT value”.) Typically, in a cart/checkout flow, things like taxes (and shipping, etc.) are shown under a (sub)total as a line item, then added to that total price to show a “total total” price.

Good questions, @Francesco! Hopefully you can understand my explanations here.


#7

Really thank you! You have been incredibly helpful!
As a last thing I ask you, can you recommend me some axure courses related to javascript functions?


#8

I don’t know of any courses or tutorials on javascript with Axure. You can find lots of good info and examples in this forum though. It is not officially supported by Axure, so you’re on your own if you use javascript injection.


#9

Hi mbc66, hope you are well!
If you like, I have another little challenge to offer you!
I have used a lot of your advice and teachings, and I am creating a new prototype that simulates the digital management of a restaurant. (You can see the result of your teachings in the “menu” page … I have explored the matter with the ability to edit and add orders and other features, you can try it if you want!)
my problems are on the waiters page for now:

  1. when I assign the first table to a waiter, the same table is shown twice in the “assigned panel” … it is very strange because if I assign a second table the problem disappears. example: I assign table A to john and in the “assigned panel” it shows AA, but if I also assign table b to him, it shows AAB, etc.
  2. (using the same example) If I have already assigned a table to john, and I don’t select a new table from the drop-down menu to assign it (thus leaving the value None), this value is also added in the “assigned panel” eg. AANone … I can’t find a way to stop it!
  3. why when I click on assign, after selecting a table, the drop-down menu returns to the default value (none), even if specific to axure to keep the value indicated by the “set selected list” option?
  4. I would like to give the user the possibility, if a table has already been assigned, to decide whether to assign it anyway or not (probably through an alert) eg. john has table A, I try to assign it to Jacob too, but an alert warns me that table A has already been assigned, and asks me if I’m sure I want to assign it anyway
  5. I can’t stop assigning the same table to the same person twice
  6. If you click the option icon, another dynamic panel status opens which allows you to update or delete a waiter.
    Do you know a method to make sure that if I open a second state of a waiter’s dynamic panel, the other dynamic panels of the repeater automatically revert to their default state?

last but not least : What do you think about my interaction skills level? Should i have a chance if i would like to candidate for an ux/ui job? ( of course with some ux skills, like usability testing, quantitative/qualitative research and analysis, design thinking, persona creation, ecc…)

Hope u’ll help me another time!

Really thank you for your time,

have a nice day!:v:esercizio_fra.rp (295.9 KB)

https://ryonpm.axshare.com


#10

This is a LOT to ask…

Here is an updated file
esercizio_fra_v2.rp (309.7 KB)

  • On your Assign button you have 4 cases. The first time a table is assigned, Case 1 (Tables // 0 to 1) fires, and Case 3 (Tables // more than 1) also fires (which you can see when you open the Console panel in the Preview and click the "Start Trace" button --this is how you can debug Axure prototypes.).
    • Both cases set "B_table" to [[Item.A_table]] but Case 3 first updates "this row" to [[Item.A_table+LVAR1]] (where LVAR1 points to selected option of droplist) …which would result in "AA"
    • Since all your cases are mutually exclusive–only 1 should apply based on your logic–you can make them "Else if" cases to ensure only one gets triggered per click.
  • It is best if you put “Update Rows …for This” as the last action, because as soon as “this same row” gets updated, everything in the repeater gets reloaded, so you can’t guarantee any code after this will get properly called.
  • I’m not sure why you have a “Wait 100 ms” before adding/updating ++B_repeater …shouldn’t be necessary, and again you should do this prior to updating “this row”.
  • The last action, "Set Selected List Option" is not necessary, as it will get set to "None" (its default) when "this row" gets updated.
  • When the droplist selection changes, it is unnecessary to set it to the same selected option, because it will get set in Item Loaded when this row is updated.
  • What is the purpose of the "A_Assigned" column? It seems redundant with "A_quantity" where if A_Quantity equals 0 then "A_Assigned" must be false, and if A_Quantity is greater than 0 then "A_Assigned" must be true.
    …unless it is somehow possible to have a quantity of tables but none "assigned" --or visa versa? Your structure and logic could be simplified (thus reducing chances for errors and/or confusion) by removing "A_Assigned"
  • You could add additional logic to your cases to include "and Selected Option of ++Table_A does not equal "None" --or better yet, make all cases "Else If" with the first case handling a selection of "None" --which I assume means it should unassign any and all tables for this waiter.
  • I don’t know what your fourth case (//more than 1 // just assigned) is supposed to do, but since it’s condition includes a table choice of “None” and more than 1 table already assigned, it would make the most sense to put it before the “// None” case, then handle cases where a table is chosen.
  • In Item Loaded you have "Set Selected Option of ++Table_A to "None" and when Assign is clicked it updates the repeater, thus Item Loaded gets fired.
  • Should the "A_table" column only track the most recent table assigned, or should it track all tables assigned to a waiter?
    • If only the most recent table, then just change the Set Selected List Option from "None" to [[Item.A_table]]
    • If it should contain all tables, in the order they are assigned, then you’d need to get only the last character from the “table string”. For example, if Jacob has tables A, B, and F then A_table would equal “ABF”. To get only the last table assigned, F, you’d use the expression [[Item.A_table.split(-1)]] . This would also work if only one table is tracked/assigned, so is the more flexible solution. I did this, along with a case that tests if [[Item.A_table.toLowerCase()]] does not equal “none” and it does not equal blank (when droplist would not need to be changed; else it would get changed to blank)
  • I’m not sure I understand the purpose of the separate Waiter list and Table list, as they seem to show the same data in slightly different ways. For instance, Waiter list shows how many tables assigned to a waiter, but not which tables. The Table list shows a concatenated list of tables, grouped by waiter, and how many tables that waiter has (why would that matter for a table?)
  • Makes sense… probably easiest to track the list of assigned tables in a global variable, like you do for numberOfTables. Then you can test if this variable contains the current droplist selection, and if so, show the alert dialog.
  • I created an alert by simply duplicating the Default state of ++Item-A, naming it “Alert” and creating a sample error string and two buttons: Cancel and Reassign. (Of course you can change this to a more standard popup dialog, but this way it keeps user focus in same place, seems like less friction. I put this on the droplist’s Selection Changed event so it is immediate. You can move it to the Assign button’s cases if you’d rather have it from that click event.
  • I reused my new "Alert" state for ++Item-A. I tried assigning it as another case on the droplist, but that conflicted with Item Load which also changes the selected option. So I moved it to a new case for the Assign button.
  • This case tests if A_table contains the selected option (thus already assigned to same waiter) and if so sets the alert text, hides the Reassign button (not applicable for this error) and leaves only the Cancel button.
  • The best way I know is to set the panel state to Default from outside the repeater. This will set every row’s instance to Default.
  • I set up a “special event function” for an unused widget, the gray box behind the table column titles, which I named “Subheader”. I used the Rotated event for this because it would otherwise not be used for anything else. So, just prior to setting the panel state for a given row, just fire Rotate of Subheader so every row’s panel state will get set to Default, then set the state to what you need for “this” row.

I also updated the behavior for the trash can in ++B_repeater to handle the ++A_repeater values when a table item is deleted.

  • I see no way to remove only table A if Jacob is assigned tables A and B. Maybe this isn’t a requirement, but seems odd that I can’t.

Best if you can have some experience with a range of different prototyping and other interaction design tools and processes. Axure allows for a lot more detailed and realistic user interactions, especially for dynamically handling user input. Other popular prototyping tools like XD, Figma, Invision, etc. allow for slick looking click-through prototypes, integrating with design systems and other tools. Each has distinct advantages and disadvantages. If you can compare and contrast them, know when and what to use which tool for, it will be much more valuable. Even if you don’t have a chance to get some experience with other tools, you can review what they do, look at and play with examples and then express desire to try them out, learn new things, etc. Also really good if you can be ready to describe what you’ve learned from prototyping, how it has made you a better UI/UX/IX designer, how you might think differently about a user interface or set of product/user requirements, how you have or would expect to work with teams, other designers, developers, etc.

Best of luck with job interviews!


#11

ou can use .each loop to iterate through your .options select-box then use $(this).data("price") to get price value and add total to your productPrice span .Also , i have added data-original-price="{{ $product->price }}" just to retain original value when calculating else it will override original value .

Demo Code :

$('.option').change(function() {
  var original_price = parseFloat($('#productPrice').data('original-price'));
  var values = 0
  var price_of_attr = 0
  //loop through all selected options..
  $('.option :selected').each(function() {
    price_of_attr += parseInt($(this).data("price")) //get data price
    //still confuse..you need value as well ? if yes use
    //values +=$(this).val() 
  })
  var finalPrice = original_price + price_of_attr //you can add + values..here in total
  $('#finalPrice').val(finalPrice);
  $('#productPrice').html(finalPrice);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>


<dt>Abc: </dt>
<dd>
  <select class="form-control form-control-sm option" style="width:180px;" name="{{ strtolower($attribute->name) }}">
    <option data-price="0" value="0"> select Abc</option>

    <option data-price="12" value="1"> Abc -1
    </option>
    <option data-price="11" value="2"> Abc -2
    </option>

  </select>
</dd>
<dt>Abc2: </dt>
<dd>
  <select class="form-control form-control-sm option" style="width:180px;" name="{{ strtolower($attribute->name) }}">
    <option data-price="0" value="0"> select Abc2</option>

    <option data-price="12" value="12"> Abc2 -12
    </option>
    <option data-price="11" value="22"> Abc2 -22
    </option>

  </select>
</dd>

<span class="currency">$</span>
<!--aded here --data-original-price="{{ $product->price }}"-->
<span class="num" id="productPrice" data-original-price="123">123</span>
```1.  First of all, create an unordered list of items with the class name "menu-item" and place the item’s name and price. Similarly, create add to cart button and place the data-price attribute to each button that will be used in the JavaScript program to count the total price.
After that, you need to set the basic style for the shopping cart interface. You can style the shopping page according to your needs. Anyhow, the following are the basic styles for the products list. You can modify these styles to get the desired output.
After creating the basic structure for the shopping cart, now it’s time to functionalize the add to cart and total price.That’s all! I hope you have successfully integrated this shopping cart minimal template into your project. If you have any questions or suggestions, let me know by comment below. The add-to-cart rate is the percentage of visitors who place at least one item in their cart during the session. Add-to-cart rates are important to track, since they can tell you about the success of your product selection, marketing efforts and site usability. Additionally, since about [30-40% of users](https://www.practicalecommerce.com/4-Things-Add-to-cart-Rates-Tell-an-Ecommerce-Business) who add an item to their cart, complete a purchase, improving these rates could mean a significant increase in revenue for your business.Understanding the average add-to-cart rates is a good first step in creating benchmarks for your business. Below are some statistics taken from our quarterly report to help you measure yourself against the industry. Add-to-cart rates are between 9%-18% globally. Great Britain’s add-to-cart held even after peaking in Q3 2020, suggesting that there is a growth opportunity, especially for US companies.

Additionally, according to [Salesforce](https://www.salesforce.com/blog/2018/07/digital-commerce-benchmarks-add-cart-rate.html), what you sell could have an influence on your add-to-cart rates. Luxury brands tend to see lower add-to-cart rates, while health &amp; beauty sites tend to have higher rates.If your add-to-cart rates are lower than the industry average (between 9-18%), there are opportunities to further improve them to increase revenue. Consider evaluating the following if trying to improve add-to-cart rates.A good selection of products is a key driver in convincing consumers to make a purchase. Consider the following steps when evaluating if your product is the problem:Is there a market for the product you are trying to sell? Trying to sell an app to a demographic that isn’t technologically savvy is an uphill battle that you may not want to fight. Doing your market research and letting these insights inform the product can help your company make better decisions. How does your product compare in terms of quality and pricing? According to Dan Ariely’s book,  *[Predictably Irrational](http://danariely.com/books/predictably-irrational/)* , people are anchored to prices based on prior experiences. When evaluating the pricing of your product, people will bring their biases with them about how much a product should cost. The only way to circumvent this is to offer something different than those in your industry. (For example: Howard Schultz created a luxury coffee brand through cambodia postal tracking , modeling his stores on European cafes). Consider what brands your clients may be comparing you to and model your pricing accordingly.