Upgrading to V2: for developers
Every V1 hook, JavaScript method, shortcode, template, endpoint and CSS hook, what happened to it in V2, and how to migrate your code.
This page is for developers and agencies who wrote code against CartPops V1 (1.5.x). It lists what is kept, changed or removed in V2, and what to use instead. Store owners should start with Upgrading to V2.
V2 references: PHP actions, PHP filters, JavaScript, Store API and REST and CSS customization.
What changed in the architecture
V1 rendered the drawer from PHP templates, loaded cart changes through admin-ajax.php actions, swapped HTML fragments into the page by CSS selector, and exposed a jQuery-based window.CartPops object. V2 renders the drawer as a server-rendered block (cartpops/cart-drawer, plus cartpops/cart-launcher), updates it on the client with the WordPress Interactivity API (a store named cartpops), and reads and changes the cart through the WooCommerce Store API and the cartpops/v1 REST namespace. There are no V1 PHP templates to override, no HTML fragments to replace, and no window.CartPops. PHP hooks that fired while V1 built markup now fire once per server render of the drawer block. They do not run again when the browser updates items, coupons, recommendations, shipping or totals.
Start here: what is most likely to break
window.CartPopsdoes not exist. Any code that touches it throws aReferenceError.cpops-toggle-drawerno longer opens the drawer. It is a styling class on CartPops’ own launcher only.- Custom JavaScript from V1 is never executed. It is quarantined in the database.
- Per-item, totals and class filters are not applied.
woocommerce_after_cart_item_name, thecartpops_cart_totals_*_htmlfilters and thecartpops_*_classesfilters do nothing. - Theme template overrides are ignored.
{theme}/cartpops/...is no longer read. - CartPops sends the single product page
form.cartin the background only when its setting is on. Add to cart on product pages without reloading (Advanced > General) is on by default. A submit handler of your own that callsevent.preventDefault()keeps the form; CartPops then does nothing. admin-ajax.phpactions and HTML fragments are gone. Use the Store API andcartpops/v1.fs_cartpops()can returnnull. Check before you call methods on it.
PHP actions
Drawer actions fire with no arguments, once per server render of the drawer block. A callback that prints markup owns its escaping, accessibility and balanced HTML. Native V2 sections are not callbacks on these actions. Priority cannot interleave your output with them, and remove_action() against a V1 native callback is now a no-op.
| Action | V2 status | What to know |
|---|---|---|
cartpops_loaded |
Kept | Fires on plugins_loaded after the container is built. It does not fire when WooCommerce is missing or when Free and Pro are both active. |
fs_cartpops_loaded |
Kept | Fires after the V2 Freemius bootstrap is ready. |
cartpops_drawer_wrapper_start, cartpops_drawer_wrapper_end |
Kept | First and last child of the interactive drawer root. The V1 ID-based wrapper is gone, but the slot is the same. |
cartpops_drawer_panel_wrapper_end |
Kept | Last child of the drawer dialog, after the footer. |
cartpops_drawer_panel_wrapper_start |
Changed | First child of the dialog, before the header. V2 no longer prints WooCommerce notices through this action, so removing the V1 notices callback does nothing. |
cartpops_drawer_header_before |
Kept | Inside the header, before the title and close button. |
cartpops_drawer_header_after |
Changed | Inside the header, after the title and close button. The Pro shipping meter is a native section, not a callback, so a V1 remove_action() of the meter does nothing. |
cartpops_drawer_content |
Changed | Fires once at the start of the scrollable content. Native sections (notices, meter, items, empty state, recommendations, totals) are not callbacks. |
cartpops_drawer_footer_before, cartpops_drawer_footer_after |
Kept | Start and end of the native footer. They fire even when the cart starts empty. The footer shows and hides itself, but your callback does not run again. |
cartpops_drawer_footer_content |
Changed | Fires after footer_before and before the native add-on, coupon, totals and checkout markup. |
cartpops_drawer_coupon_wrapper_start, cartpops_drawer_coupon_wrapper_end, cartpops_drawer_coupon_form_before |
Kept | Fire only when the coupon setting is on. |
cartpops_drawer_coupon_form_after |
Changed | Applied-coupon tags are native and follow this position. Removing the V1 tag callback does nothing. |
cartpops_drawer_before_checkout_button, cartpops_drawer_after_checkout_button |
Kept | Before and after the primary checkout link. |
cartpops_drawer_before_secondary_checkout_button, cartpops_drawer_after_secondary_checkout_button |
Changed | They fire below the checkout button in V2. V1 printed them above it. They fire in both editions, even when no secondary action exists. The secondary action itself is Pro. |
For the full position table, see PHP actions.
Removed actions
The actions below have no V2 equivalent. Popup, bar and assistant were Pro-only in V1, so Free sites never had them.
Popup, bar and assistant actions
The beta popup and bar and the V1 assistant are retired. Their render lifecycle does not exist in V2.
cartpops_popup_wrapper_start, cartpops_popup_wrapper_end
cartpops_recommendation_popup
cartpops_cart_trigger_contents, cartpops_last_cart_item
cartpops_free_shipping_meter_horizontal, cartpops_powered_by
cartpops_assistant_wrapper_start, cartpops_assistant_wrapper_end
cartpops_assistant_panel, cartpops_assistant_panel_top, cartpops_assistant_panel_bottom
cartpops_assistant_panel_wrapper_start, cartpops_assistant_panel_wrapper_endFor static additions, use the drawer positions in the table above. The V1 assistant is replaced by the inline shipping calculator in the Pro drawer, which has no action API.
Rules engine and rule admin screens
V1 automation rules (the plural cartpops_rules post type) are retired and not converted. Their admin and lifecycle hooks are gone.
cartpops_after_created_new_rule, cartpops_after_updated_rule
cartpops_before_rule_general_settings, cartpops_after_rule_general_settings
cartpops_before_rule_criteria_settings, cartpops_after_rule_criteria_settings
cartpops_before_rule_filters_settings, cartpops_after_rule_filters_settings
cartpops_before_rule_restrictions_settings, cartpops_after_rule_restrictions_settings
cartpops_before_rule_notices_settings, cartpops_after_rule_notices_settings
cartpops_rule_data_panels, cartpops_update_rule_order_count, cartpops_update_rule_user_usage_countThe rules list table filters (cartpops_rules_get_columns and similar) are gone with it. V2 has a different, separate cartpops_rule model, and the upgrade does not convert V1 rows into it.
Admin settings framework
V1 built its admin screens with a PHP settings framework. V2 uses a React admin and a REST settings endpoint. These hooks are gone.
cartpops_admin_settings_menu_title, cartpops_admin_settings_page_title
cartpops_admin_settings_sanitize_option, cartpops_settings_fields
cartpops_settings_menu_items, cartpops_settings_tabs_array
cartpops_default_settings, cartpops_page_screen_ids, cartpops_plugin_issues_list
cartpops_before_shortcode_contents, cartpops_after_shortcodes_contentAlso gone: every tab, section, save, reset and field-render hook that was built from the cartpops_settings_* and cartpops_admin_field_* name patterns.
PHP filters
Kept or changed
| Filter | V2 status | What to know |
|---|---|---|
cartpops_checkout_button_url |
Changed | Same single-value call. The result must be an absolute HTTP or HTTPS URL with a host, or a root-relative path such as /express. Anything else falls back to the WooCommerce checkout URL. Return a raw URL, not HTML. |
cartpops_empty_cart_button_url |
Changed | Same rules as above, applied to the Continue shopping link. |
cartpops_add_to_cart_trigger |
Changed | Canonical values are add_to_cart and launcher. V1 both, drawer, popup and bar become add_to_cart. manual and none become launcher. A retired popup or bar callback therefore now opens the drawer. |
cartpops_powered_by_link |
Changed | Runs only when Powered by CartPops is on and a partner code is stored, and receives the Freemius partner URL. V1 ran it unconditionally with a CartPops URL as the default, so callbacks that rewrote that default no longer run. |
cartpops_secondary_btn_classes |
Changed, Pro | Additive only. The callback receives the CartPops classes and the mode (continue_shopping, view_cart or custom_url). You cannot remove the owned classes. The result is at most 16 valid CSS identifiers, and any invalid result restores the full CartPops list. |
cartpops_elementor_widget_cart_is_hidden |
Kept, Pro | The result is cast to a boolean. |
Replaced
| V1 filter or hook | Use in V2 |
|---|---|
cartpops_after_cart_item_name_hook_collapsible |
cartpops_cart_item_data. V2 has no collapsible details block. A callback can empty or rewrite extraLines. |
woocommerce_after_cart_item_name (action) |
cartpops_cart_item_data (extraLines), or WooCommerce’s woocommerce_get_item_data, which CartPops already reads into the same row. See Add a line under the product name. |
cartpops_is_valid_product |
woocommerce_widget_cart_item_visible, which V2 still applies. |
New in V2
| Filter | What it does |
|---|---|
cartpops_cart_item_data |
Adjust a cart row’s text, extra lines and a bounded custom presentation map. Three arguments: the presentation row, the raw cart item and the cart item key. |
cartpops_store_api_cart_extension_data, cartpops_store_api_cart_extension_schema |
Add bounded data to CartPops’ Store API extension namespace. |
cartpops_pro_recommendation_button_presentation (Pro) |
Change the recommendation add button mode and text for entitled sites. |
cartpops_render_drawer |
Return false to stop the automatic drawer that CartPops renders in the footer on classic themes. |
cartpops_menu_launcher_attributes |
Adjust the bounded launcher attributes for a cpops-cart-menu-item menu item. |
cartpops_custom_css_style_dependencies |
Adjust the style dependencies of the stylesheet that carries your Custom CSS. |
cartpops_rest_allowed_origins, cartpops_rest_trusted_proxy_cidrs |
Adjust which origins and proxies the CartPops REST protections accept. A CDN or reverse proxy can otherwise cause rate-limit errors. |
Removed
Class and HTML filters (documented in V1)
V2 does not apply any of these. The elements keep many V1 class names, so scoped CSS often does the job. See Add classes.
cartpops_drawer_classes, cartpops_drawer_empty_cart_classes
cartpops_drawer_header_classes, cartpops_drawer_footer_classes
cartpops_drawer_coupon_form_classes, cartpops_drawer_cart_totals_classes
cartpops_drawer_recommendations_classes, cartpops_drawer_cart_item_classes
cartpops_single_line_item_classes, cartpops_powered_by_classes
cartpops_cart_launcher_classes, cartpops_floating_cart_launcher_classes
cartpops_drawer_shipping_meter_classes, cartpops_shipping_classescartpops_secondary_btn_attributes is also removed. Raw attribute strings cannot be made safe with V2’s reactive and accessibility contract. To add a link with attributes such as target, print your own element with cartpops_drawer_before_secondary_checkout_button or cartpops_drawer_after_secondary_checkout_button.
Totals, price and notice filters
V2 formats totals in the browser from WooCommerce’s numeric values, so there is no HTML to filter. To change a total, change the underlying WooCommerce price, fee, tax or shipping calculation.
cartpops_cart_totals_subtotal_html, cartpops_cart_totals_order_total_html
cartpops_cart_totals_vat_total_html, cartpops_cart_totals_discount_total_html
cartpops_cart_totals_shipping_html
cartpops_cart_item_price_display, cartpops_after_cart_item_name_price
cartpops_drawer_notice_html, cartpops_notices_html, cartpops_kses_notice_allowed_tagscartpops_after_cart_item_name_price used to switch whether the price shows after the item name. V2 always shows the price in the row.
Behavior, timing and fragment filters
cartpops_notification_remove_delay, cartpops_loading_state_delay
cartpops_generic_error_message
cartpops_cart_item_removed_title, cartpops_cart_item_removed_notice_type
cartpops_ajax_fragments, cartpops_available_cart_fragments
cartpops_bundled_items_data, cartpops_get_product
cartpops_template, cartpops_template_pathThe closest V2 concept to cartpops_notification_remove_delay is the Undo window after removing an item. It is fixed at 5000 ms and is not filterable. Bundled and add-on cart lines are recognized from a fixed list of markers, not through cartpops_bundled_items_data.
Popup, bar, assistant and upsell-table filters
cartpops_alt_trigger_classes, cartpops_last_cart_item_classes
cartpops_popup_recommendations_classes
cartpops_assistant_content, cartpops_assistant_content_classes
out_of_stock_add_to_cart_text, out_of_stock_add_to_cart_url
cartpops_pagination_classes, cartpops_pagination_number
cartpops_get_dropdown_upsell_product_name, cartpops_show_dropdown_add_to_button
cartpops_upsell_product_add_to_cart_button_label
cartpops_upsell_product_dropdown_default_value_label
cartpops_upsell_product_heading_labelRules, post types and admin framework filters
cartpops_add_custom_post_types, cartpops_add_custom_post_status
cartpops_rules_post_type_args, cartpops_master_log_post_type_args
cartpops_active_post_status_args, cartpops_inactive_post_status_args
cartpops_manual_post_status_args, cartpops_automatic_post_status_args
cartpops_rule_statuses, cartpops_master_log_statuses, cartpops_paid_order_statuses
cartpops_rule_data_tabs, cartpops_rule_translate_stringThe cartpops_master_log post type is no longer registered. Its rows are left in place.
The documented V1 names cartpops_drawer_inside (it never fired in V1), cartpops_cart_totals_subtotal_total_html (a typo for the subtotal filter) and the callback names cartpops_checkout_url and cartpops_empty_cart_url in old docs were never real hooks.
WooCommerce hooks in the drawer
V1 re-fired many WooCommerce hooks while it built its own cart rows. V2 builds rows from WooCommerce’s cart data and Store API, so only these hooks still matter.
| WooCommerce hook | V2 behavior |
|---|---|
woocommerce_widget_cart_item_visible |
Kept. Same three arguments. A non-scalar result or an exception hides the row. |
woocommerce_get_item_data |
Kept. CartPops reads it into the row’s extra lines. |
woocommerce_cart_item_backorder_notification, woocommerce_cart_item_product_id |
Still applied, with a narrower scope. The backorder text must be a string, bounded to 500 bytes, and is shown as plain text. |
woocommerce_cart_item_permalink |
Partial. WooCommerce’s Store API applies it, so it takes effect after a refresh, but not on the first server render. |
woocommerce_cart_item_thumbnail |
Not applied. woocommerce_store_api_cart_item_images changes images after a refresh on WooCommerce 9.6 and newer, not on the first render. |
woocommerce_quantity_input_min, woocommerce_quantity_input_max, woocommerce_quantity_input_step |
Version dependent. Use woocommerce_store_api_product_quantity_minimum, woocommerce_store_api_product_quantity_maximum and woocommerce_store_api_product_quantity_multiple_of, which V2 relies on. |
woocommerce_cart_item_name, woocommerce_cart_item_subtotal, woocommerce_cart_item_product |
No effect on the drawer. Use cartpops_cart_item_data for the name and extra lines. |
woocommerce_quantity_input_classes, _pattern, _inputmode, _placeholder |
No effect. V2 has plus and minus buttons and a text value, with no input element. |
woocommerce_cart_no_shipping_available_html, woocommerce_shipping_estimate_html, woocommerce_shipping_may_be_available_html, woocommerce_shipping_not_enabled_on_cart_html, woocommerce_after_shipping_rate |
Not applied. The drawer shows a shipping string derived from WooCommerce. |
woocommerce_add_to_cart_fragments |
WooCommerce still applies it for its own refreshes, so your callbacks still run. CartPops adds one fragment, .cartpops-cart-json, and no longer adds HTML fragments. |
woocommerce_ajax_added_to_cart |
WooCommerce fires it for its own AJAX adds. Adds that CartPops starts (Store API, recommendation add, bundle add) do not fire it. Trackers should use WooCommerce’s woocommerce_add_to_cart instead. |
JavaScript API
V1 exposed window.CartPops with drawer, popup, bar and assistant objects. V2 has no global object. Classic scripts talk to the drawer through DOM events on document.
| V1 | V2 |
|---|---|
window.CartPops |
Removed. Referencing it throws. |
CartPops.drawer.show() |
Dispatch cartpops:open on document. |
CartPops.drawer.hide() |
Dispatch cartpops:close. |
CartPops.drawer.toggle() |
Dispatch cartpops:toggle. |
CartPops.drawer.on('show') and on('hide') |
No equivalent. V2 does not emit an open or close event. |
CartPops.popup, CartPops.bar |
Removed with the retired presentations. |
CartPops.assistant.show() |
Removed. The Pro shipping calculator is inline in the drawer. |
window.CartPopsConfig |
Removed. V2 publishes its configuration as Interactivity API state, not as a global. |
$(document.body).trigger('adding_to_cart') and 'added_to_cart' fired by CartPops |
CartPops no longer triggers them. WooCommerce still fires added_to_cart for its own AJAX adds. |
V2 browser events:
| Event | Direction | What it does |
|---|---|---|
cartpops:open |
You dispatch it on document |
Opens the drawer. |
cartpops:close |
You dispatch it on document |
Closes the drawer. |
cartpops:toggle |
You dispatch it on document |
Toggles the drawer. |
cartpops:count-updated |
CartPops dispatches it on document |
Fires after the cart count changes. The count is in event.detail.count. |
Things to know:
- The
cartpops:open,cartpops:closeandcartpops:togglelisteners are installed when the drawer’s script module loads. An event dispatched earlier, such as atDOMContentLoaded, is ignored. Dispatch from a user action, such as a click. - V2 has no
cartpops:ready,cartpops:openedorcartpops:closedevent. To react to the drawer opening, you have no supported signal. - CartPops still listens for WooCommerce’s jQuery
added_to_cartevent to open the drawer, but only ifwindow.jQueryexists when its script module runs. A plugin that delays or defers jQuery can stop it from hearing classic AJAX adds. - Adds that CartPops makes itself, such as recommendation or bundle adds, do not fire jQuery
added_to_cart. Analytics ordataLayercode that listens for it will miss them. data-cpops-cart-open="false"on an add to cart button still stops that one add from opening the drawer.- Quarantined V1 custom JavaScript is never executed. Move it into your theme or a plugin and rewrite it against the events above.
Shortcodes and menu items
| V1 | V2 status | What to know |
|---|---|---|
[cartpops_cart_launcher] |
Kept | Same tag. The output is new markup: a button with the class cpops-launcher, not the V1 div.cartpops-cart__container. |
[cartpops_launcher] |
New | The current tag. Same renderer. |
icon attribute |
Changed | Values are cart, bag and basket. V1 cpops-icon-* names map to the nearest one. A custom string, such as icon="my-icon" with CSS on .cartpops-cart__container-icon i, falls back to cart. That recipe no longer works. |
subtotal, indicator, indicator_hide_empty |
Kept | Same values. |
hide_empty |
New | Hides the whole launcher while the cart is empty. |
wrapper, shortcode |
Removed | Internal V1 flags, now ignored. |
Menu class cpops-cart-menu-item |
Kept, changed | CartPops replaces the whole item output with a launcher. The V1 <a class="menu-link"> wrapper is gone. Classic menus only, not the Navigation block. The new cartpops_menu_launcher_attributes filter adjusts it. |
Menu or element class cpops-toggle-drawer |
Removed as a trigger | Still present on the launcher button as a style class. |
Elementor widget cartpops-cart (Pro) |
Kept | Widget identity and the control IDs sampled in review are preserved, so saved pages keep the widget. Spot-check the icon values on your pages. |
Shortcode launchers take their colors from Launcher > Inline launcher appearance, not from attributes.
Templates and overrides
V1 let a theme override templates in {theme}/cartpops/{slug}.php. The assistant template loaded from {theme}/woocommerce/premium/assistant.php. V2 has no template loader and does not read either location. A copied V1 cartpops folder is dead code and does no harm.
| V1 template group | V2 |
|---|---|
drawer/drawer, header, footer, cart-totals, coupon-form, empty-state, recommendations, shipping |
Replaced by the cartpops/cart-drawer block render and a client module. shipping is replaced by the Pro inline calculator. |
drawer/cart-item |
Replaced by a client template that renders from state. Use cartpops_cart_item_data for text. |
components/launcher, components/powered-by |
Replaced by the launcher block and the Powered by link logic. |
components/product/price, components/product/quantity-selector |
Removed. The browser formats prices, and the quantity buttons are part of the drawer module. |
premium/cartpops-shipping-meter |
Replaced by the Pro Free Shipping Meter block. The circular meter is dropped. |
premium/assistant*, premium/cartpops-popup*, premium/cartpops-bar*, premium/cartpops-last-added-item |
Removed with the retired features. |
The removed loader functions are cartpops_get_template(), cartpops_get_theme_template(), cartpops_get_plugin_template() and cartpops_template_path(), plus the cartpops_template and cartpops_template_path filters.
There is no supported way to replace the drawer’s markup. Use the action positions for static additions, cartpops_cart_item_data for row text, and CSS for appearance.
Global functions, constants and classes
| V1 | V2 status | Use instead |
|---|---|---|
cartpops_get_cart_item_count(), cartpops_get_wc_cart_subtotal(), cartpops_get_wc_cart_total(), cartpops_price() |
Removed | WC()->cart->get_cart_contents_count(), get_cart_subtotal(), get_cart_total() and wc_price(). |
cartpops_get_template() and the other template loaders |
Removed | See Templates and overrides. |
fs_cartpops(), cartpops_fs() |
Kept, changed | Both return ?object. They can return null. Check before you call a method. |
CARTPOPS_VERSION, CARTPOPS_PATH, CARTPOPS_URL |
Kept | |
CARTPOPS_FILE |
Changed | V1 stored plugin_basename(). V2 stores the absolute __FILE__ path. |
CARTPOPS_PREFIX, CARTPOPS_ABSPATH, CARTPOPS_DOCS, CARTPOPS_TEMPLATE_DEBUG_MODE, cartpops_plugin_path() |
Removed | Use CARTPOPS_PATH and CARTPOPS_URL. |
CartPops_Cart::render_cart_launcher() |
Removed | echo do_shortcode( '[cartpops_launcher]' ); |
CartPops\Admin\Options::get() and the other V1 settings classes |
Removed | There is no documented PHP API for reading CartPops settings. |
V1 classes such as CartPops_Frontend_Ajax, CartPops_Public, CartPops_Assets, CartPops_Settings |
Removed | V2 classes are namespaced, mostly final, and are not a public API. |
Removed V1 helper functions (rules, admin and layout)
These were internals of the rules engine, admin screens and upsell tables. None was documented, and none exists in V2.
cartpops_get_wc_cart_category_subtotal, cartpops_get_product_count_in_cart
cartpops_get_buy_product_count_in_cart, cartpops_get_coupon_upsell_product_count_in_cart
cartpops_get_free_upsell_products_count_in_cart, cartpops_get_rule_products_count_in_cart
cartpops_get_upsell_products_in_cart, cartpops_get_free_upsells_per_page_column_count
cartpops_get_carousel_options, cartpops_get_rule_translated_string
cartpops_get_address_metas, cartpops_get_address, cartpops_get_product
cartpops_cart_item_price, cartpops_cart_item_thumbnail, cartpops_cart_item_product
cartpops_cart_item_product_name, cartpops_get_quantity_from_cart_item
cartpops_after_cart_item_name, cartpops_render_product_image
cartpops_implode_html_attributes, cartpops_mapped_implode, cartpops_check_is_array
cartpops_outbound_url, cartpops_get_emoji_flag, cartpops_set_cart_constant
cartpops_create_new_rule, cartpops_get_rule, cartpops_update_rule, cartpops_delete_rule
cartpops_create_new_master_log, cartpops_get_master_log, cartpops_update_master_log
cartpops_delete_master_log, cartpops_select2_html, cartpops_get_datepicker_htmlPlus the remaining pagination, upsell-label and admin-page helpers.
AJAX endpoints and fragments
V1 registered ten admin-ajax.php actions, each as wp_ajax_ and wp_ajax_nopriv_. They returned HTML fragments keyed by CSS selector and did not verify a nonce. None was documented. V2 removes all of them.
V1 admin-ajax.php action |
V2 replacement |
|---|---|
cpops_add_to_cart |
WooCommerce’s own add to cart, or the Store API POST /wc/store/v1/cart/add-item. |
cpops_refresh_cart, cpops_force_refresh_fragments |
GET /wc/store/v1/cart or GET /cartpops/v1/cart. The V1 force-refresh option is now Advanced > General > Performance > Refresh cart data on page load. |
cpops_update_cart |
Store API update-item. |
cpops_remove_product, cpops_restore_product |
Store API /batch with remove-item, or POST /cartpops/v1/cart/remove-items. Undo is a client action. |
cpops_apply_coupon, cpops_remove_coupon |
POST /cartpops/v1/coupon and DELETE /cartpops/v1/coupon, each with a code. |
cpops_calculate_shipping, cpops_update_shipping_method (Pro) |
The inline shipping calculator in the Pro drawer. There is no direct replacement endpoint. |
Admin helpers such as cartpops_json_search_products |
Admin REST routes under cartpops/v1, such as /settings and /products/search. |
V2 requests authenticate with a WooCommerce Cart-Token header and a nonce. CartPops also accepts requests only from your site’s own origins and rate limits them per client. See Store API and REST. V1 registered no wc-ajax handlers of its own.
Fragments
V1 filtered woocommerce_add_to_cart_fragments to return HTML keyed by selector, and its JavaScript called replaceWith() for each one. These keys are gone:
.cpops-drawer-cart, div.cpops-drawer-notices-wrapper, div.cpops-cart-total
div.cpops-coupon-remove, div.cpops-drawer-recommendations-wrapper
div#cpops-floating-cart, .cpops-free-shipping-meter
.cartpops-cart__container-counter, .cartpops-cart__container-text
.cpops-popup-recommend-products, .cpops-added-product-item, div.cpops-assistant-items
V2 emits a single fragment, .cartpops-cart-json: an inert script element of type application/json that carries lightweight cart state, so a classic add can skip a second request. The visible cart updates from Interactivity API state.
Triggers, IDs and data attributes
| V1 | V2 status | What to know |
|---|---|---|
Class cpops-toggle-drawer on any element or menu item |
Removed as a trigger | There is no click handler for it. See Open the drawer from a custom button. |
Click on a WooCommerce a.added_to_cart (“View cart”) link opened the drawer |
Removed | The link goes to the cart page. |
Submit on form.cart sent through AJAX |
Kept, behind a setting | With Add to cart on product pages without reloading on, CartPops posts the form’s own fields to the form’s URL, so WooCommerce’s normal add to cart handling and validation run. It then fires added_to_cart with fresh fragments, like a shop page add. It does not fire adding_to_cart. File upload, external, grouped and redirect-to-cart cases, failed requests, and submits another handler already prevented are left to the browser. |
.cpops-toggle-assistant with data-assistant="shipping" |
Removed | The assistant is gone. |
data-cpops-cart-open="false" on an add button |
Kept | Skips the automatic open for that add. |
data-cpops-type="recommendation", data-cpops-toggle="collapse", data-cpops-target, data-dismiss="cpops-modal" |
Removed | |
IDs #cpops-drawer-modal, #cartpops-drawer, #cpops-floating-cart, #cartpops-cart-launcher-<n> |
Removed | V2 uses request-unique drawer IDs such as cpops-drawer-<n> for aria-controls, and does not give launchers an ID. Select on classes. |
IDs #cartpops-popup, #cartpops-bar, #cartpops-assistant and their modal IDs |
Removed | Retired features. |
html.cpops-trigger-open scroll lock class |
Removed | V2 sets an inline overflow: hidden on body. |
.cpops-show, .cpops-is-closing, .cpops-fade animation classes |
Removed | Open state is the class cpops-drawer-open on .cpops-modal and .cpops-drawer. |
V1 had no URL, hash or query-string trigger, and V2 has none either.
CSS classes and custom properties
The class prefixes are unchanged: cpops- and cartpops-. The structure underneath changed, so only part of the V1 class set exists in V2. Your Custom CSS is migrated and loaded after the CartPops styles. See CSS customization.
Kept as aliases on the current element:
| V1 class | Now on |
|---|---|
.cpops-modal |
The block wrapper that holds the overlay and drawer |
.cpops-modal-backdrop |
.cpops-overlay |
.cpops-default-drawer, .cpops-modal-wrap, .cpops-panel |
.cpops-drawer, all on one element, so selectors that relied on them being nested no longer match |
.cpops-drawer-header (and __heading, __title, __close) |
.cpops-drawer__header, .cpops-drawer__title, .cpops-drawer__close |
.cpops-drawer-cart, .cpops-empty-cart, .cpops-drawer-footer |
.cpops-drawer__content, .cpops-drawer__empty, .cpops-drawer__footer |
.cartpops-cart__toggle, .cartpops-cart__container, .cpops-toggle-drawer |
The launcher button (styling only) |
.cpops-floating-cart__button, .cpops-floating-cart__count |
The floating launcher button and its count |
.cartpops-cart--items-indicator-*, .cartpops-cart--empty-indicator-hide, .cartpops-cart--show-subtotal-yes |
The launcher button, matching the current mode |
.cartpops-cart__container-counter, .cartpops-cart__container-text |
The launcher count and subtotal |
.cpops-free-shipping-meter (and __text), .cpops-shipping-progress-bar (and __active, __progress) (Pro) |
The horizontal meter, its message and its bar |
Removed: the V1 cart row internals (.cpops-cart-item__quantity*, __product*, .cpops-cart-line-items, .cpops-restore-item), totals and coupon-tag wrappers, notices, the Splide slider (.cpops-slider*), the V1 component kit (.cpops-button, .cpops-label*, .cpops-collapse*, .cpops-tooltip*), the circular meter, and all popup, bar and assistant classes. Selectors that depend on V1 wrapper depth, sibling order or :nth-child are unsupported.
Custom properties: V1 --color-cpops-* and --cpops-width-drawer-* variables set on :root keep working through a two-way bridge, and the live element exposes the effective value under the V1 name too. Setting a V1 variable on a narrower element is not supported. Use the current V2 name. --cpops-animation-duration and --cpops-border-radius kept their names. Removed: --color-cpops-button-hover-opacity, --color-cpops-loader-color, --cpops-white-space-text (now the Product name display setting), and the slider and popup colors.
V1 variable (on :root) |
V2 variable |
|---|---|
--color-cpops-accent-color |
--cpops-primary |
--color-cpops-background-primary |
--cpops-background |
--color-cpops-text-primary |
--cpops-text-primary |
--color-cpops-button-primary-background |
--cpops-button-primary-bg |
--cpops-width-drawer-desktop |
--cpops-drawer-width |
--cpops-width-drawer-mobile |
--cpops-drawer-width-mobile |
The full variable mapping lives in CSS customization.
Code migrations
Open the drawer from a custom button
In V1, any element with cpops-toggle-drawer, or a call to CartPops.drawer.show(), opened the drawer.
jQuery( '.my-cart-button' ).on( 'click', function ( e ) {
e.preventDefault();
CartPops.drawer.show();
} );
In V2, dispatch cartpops:open on document when the user clicks.
document.addEventListener( 'click', function ( event ) {
if ( ! event.target.closest( '.my-cart-button' ) ) {
return;
}
event.preventDefault();
document.dispatchEvent( new CustomEvent( 'cartpops:open' ) );
} );
Give the link a real href (your cart page) so it still works if the click happens before the drawer script has loaded. Use cartpops:close and cartpops:toggle the same way.
Keep a header counter in sync
V1 code usually listened for jQuery cart events or returned its own WooCommerce fragment. In V2, render the starting count on the server and update it from cartpops:count-updated.
<span class="my-cart-count">
<?php echo (int) ( WC()->cart ? WC()->cart->get_cart_contents_count() : 0 ); ?>
</span>
document.addEventListener( 'cartpops:count-updated', function ( event ) {
document.querySelectorAll( '.my-cart-count' ).forEach( function ( el ) {
el.textContent = event.detail.count;
} );
} );
A fragment-based counter can keep working, because WooCommerce still applies woocommerce_add_to_cart_fragments and CartPops asks WooCommerce to refresh fragments after its own changes. That depends on WooCommerce’s cart-fragments script being loaded, and many themes and speed plugins remove it. The event does not depend on that script.
Add a line under the product name
In V1, you printed HTML after the item name.
add_action( 'woocommerce_after_cart_item_name', function ( $cart_item, $cart_item_key ) {
if ( ! empty( $cart_item['my_engraving'] ) ) {
echo '<div class="my-engraving">Engraving: ' . esc_html( $cart_item['my_engraving'] ) . '</div>';
}
}, 10, 2 );
In V2, add a row to extraLines with cartpops_cart_item_data.
add_filter(
'cartpops_cart_item_data',
static function ( array $item, array $cart_item, string $cart_key ): array {
if ( empty( $cart_item['my_engraving'] ) ) {
return $item;
}
$item['extraLines'][] = array(
'label' => __( 'Engraving', 'my-plugin' ),
'value' => (string) $cart_item['my_engraving'],
);
return $item;
},
10,
3
);
If your data is already exposed through WooCommerce’s standard woocommerce_get_item_data filter, CartPops shows it in the drawer without extra code.
extraLines are plain text. Tags and control characters are stripped, and the drawer never runs row HTML. You can add at most 20 rows, with a label up to 160 bytes and a value up to 1,000 bytes. CartPops ignores overrides of price, quantity, key, link and image. The same filter also runs for Store API responses, so the lines survive quantity and remove updates. For custom client code, customPresentation carries a bounded map under your own plugin key. See PHP filters.
Change the checkout URL
The filter name and call are the same as in V1.
add_filter( 'cartpops_checkout_button_url', function ( $url ) {
return home_url( '/express-checkout/' );
} );
What changed is validation. CartPops accepts an absolute HTTP or HTTPS URL with a host, or a root-relative path such as /express-checkout/. A bare relative value such as express-checkout, a protocol-relative URL, a URL with credentials or an unsupported scheme falls back to the WooCommerce checkout URL. Return a raw URL, not HTML. cartpops_empty_cart_button_url follows the same rules.
Add classes
V1 filters such as cartpops_drawer_classes are not applied in V2. There is no filter that adds classes to CartPops-owned elements, and an action cannot do it either. Use CSS. Many elements keep their V1 class names, so you can scope your rules with a class on body.
add_filter( 'body_class', function ( $classes ) {
$classes[] = 'my-cartpops-skin';
return $classes;
} );
.my-cartpops-skin .cpops-drawer {
--cpops-primary: #1d4ed8;
}
In Pro, the secondary drawer button still supports extra classes through cartpops_secondary_btn_classes. It is additive only.
add_filter( 'cartpops_secondary_btn_classes', function ( array $classes, string $mode ): array {
$classes[] = 'my-secondary-button';
return $classes;
}, 10, 2 );
Guard fs_cartpops()
fs_cartpops() and cartpops_fs() return null when the Freemius bootstrap did not complete. A V1 snippet that calls a method directly on the result can fail with a fatal error.
$fs = function_exists( 'fs_cartpops' ) ? fs_cartpops() : null;
if ( null === $fs ) {
return;
}