Skip to content
CartPops Docs
Esc
↑↓navigate↵open⌘Jpreview
On this page

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

  1. window.CartPops does not exist. Any code that touches it throws a ReferenceError.
  2. cpops-toggle-drawer no longer opens the drawer. It is a styling class on CartPops’ own launcher only.
  3. Custom JavaScript from V1 is never executed. It is quarantined in the database.
  4. Per-item, totals and class filters are not applied. woocommerce_after_cart_item_name, the cartpops_cart_totals_*_html filters and the cartpops_*_classes filters do nothing.
  5. Theme template overrides are ignored. {theme}/cartpops/... is no longer read.
  6. CartPops sends the single product page form.cart in 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 calls event.preventDefault() keeps the form; CartPops then does nothing.
  7. admin-ajax.php actions and HTML fragments are gone. Use the Store API and cartpops/v1.
  8. fs_cartpops() can return null. 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.

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_count

The 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_content

Also 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_classes

cartpops_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_tags

cartpops_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_path

The 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.

Rules, 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_string

The 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:close and cartpops:toggle listeners are installed when the drawer’s script module loads. An event dispatched earlier, such as at DOMContentLoaded, is ignored. Dispatch from a user action, such as a click.
  • V2 has no cartpops:ready, cartpops:opened or cartpops:closed event. To react to the drawer opening, you have no supported signal.
  • CartPops still listens for WooCommerce’s jQuery added_to_cart event to open the drawer, but only if window.jQuery exists 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 or dataLayer code 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_html

Plus 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;
}

Was this page helpful?