WooCommerce Lean Cart
A minimal, modular WooCommerce cart built on the Store API. Replaces legacy cart fragments with a vanilla JS state layer and event-driven UI.
by Lasse Jellum · github.com/nytafar/woocommerce-lean-cart · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/nytafar/woocommerce-lean-cart/archive/refs/heads/main.zipA modern, performant, and themable cart for WooCommerce — built on first principles and the direction of WooCommerce core.
Lean Cart replaces the legacy cart-fragments system with a vanilla JS state layer powered by the WooCommerce Store API. No jQuery, no build step, no PHP-rendered HTML blobs. Just ES modules, JSON, and events.
Why Lean Cart
WooCommerce's default cart relies on cart fragments — a system that re-renders HTML on the server and ships it back via AJAX on every page load. It holds a PHP session lock that blocks concurrent requests, adds 200–400ms of latency to every navigation, and forces themes to template cart markup in PHP.
Lean Cart takes a different approach:
- Store API native — reads and writes cart state through
wc/store/v1/, the same API that powers WooCommerce Blocks and the Checkout Block. No admin-ajax, nowc-ajax, no fragments. - Vanilla ES modules — zero dependencies, no build step, no jQuery. Ships as native
type="module"scripts with an import map for cache busting. - Event-driven architecture — the store emits
lean-cart:updated,lean-cart:open,lean-cart:errorevents ondocument. Themes subscribe to render however they want. - Cold-fetch guard — skips the initial cart fetch entirely for visitors without a WooCommerce session cookie. First-time visitors pay zero cart overhead.
- Optimistic UI — quantity changes, additions, and removals update the UI instantly and roll back on failure.
- Skeleton CSS with theme tokens — ships a functional layout that maps to CSS custom properties. Themes override visuals; the plugin handles structure.
Description
Lean Cart is a drop-in WooCommerce cart replacement built for developers who want full control over the cart experience without fighting legacy infrastructure.
It intercepts native add-to-cart forms and loop links, converts them into Store API calls, and maintains a single source of truth in a JS state store. The UI layer finds [data-lean-cart] mount points in your theme's markup and hydrates them — no shortcodes, no widgets, no blocks required.
Architecture
cart-init.js Entry point — boots store, UI, modules
├── cart-store.js State management, normalizer pipeline, form extractors
├── cart-api.js All Store API network calls, nonce management
├── cart-add.js Intercepts form.cart submits and loop links
├── cart-ui.js Pure renderer — subscribes to events, writes to DOM
└── modules/ Conditionally loaded extensions
├── subscriptions.js
├── mix-and-match.js
└── all-products-subs.js
Key design decisions
- No build step. The JS ships as authored. ES module imports resolve at runtime via an import map printed in
<head>. Cache busting uses file modification timestamps. - Styling lives in the theme. The plugin ships skeleton CSS scoped under
.lean-cartthat maps to CSS custom properties (--lc-text,--lc-border,--lc-accent, etc.) with standalone fallbacks. Themes set these tokens or dequeue the stylesheet entirely viaadd_filter('lean_cart_disable_styles', '__return_true'). - Modular plugin compatibility. Extension modules are loaded conditionally based on class existence checks — zero overhead when the parent plugin isn't active.
- Form extractor pipeline. Modules can register functions via
store.addFormExtractor()that read plugin-specific form inputs and inject extra fields into the Store API add-to-cart body. This keeps core generic while supporting complex product types. - Normalizer pipeline. Modules register normalizers via
store.addNormalizer()to enrich the display model with badges, subscription summaries, parent-child grouping, and purchase mode indicators.
Plugin compatibility
Lean Cart includes native modular support for:
- WooCommerce Subscriptions — billing period summaries, signup fee and trial period meta rows, subscription badges.
- WooCommerce Mix and Match Products — form extractor for
mnm_config, parent-child grouping in the cart, container badges. Includes a server-side guard that rejects misconfigured containers. - All Products for WooCommerce Subscriptions — custom Store API extension exposing active subscription schemes, purchase mode display (one-time vs. subscription).
Adding support for a new plugin means creating one PHP class in includes/modules/ and one JS module in assets/js/modules/ — no core changes required.
Theme integration
Lean Cart doesn't render a drawer, panel, or page. It hydrates mount points your theme provides:
<!-- Anywhere in your theme -->
<div class="lean-cart">
<div data-lean-cart="items"></div>
<div data-lean-cart="totals"></div>
<div data-lean-cart="empty" hidden>Your cart is empty.</div>
<div data-lean-cart="error" hidden></div>
<a data-lean-cart="checkout-url" href="#">Checkout</a>
</div>
<!-- Badge on a menu icon -->
<span data-lean-cart="count">0</span>
<span data-lean-cart="label">0 items</span>
The plugin emits a lean-cart:open event when an item is added. If your theme exposes window.numenu, it bridges automatically. Otherwise, listen to the event directly:
document.addEventListener('lean-cart:open', () => {
document.querySelector('.my-cart-drawer').classList.add('open');
});
Public JS API
window.leanCart.store // Full store access (getState, addItem, removeItem, etc.)
window.leanCart.refresh() // Re-fetch cart from Store API
window.leanCart.open() // Dispatch lean-cart:open event
window.leanCart.close() // Dispatch lean-cart:close event
Installation
- Upload the
woocommerce-lean-cartfolder towp-content/plugins/. - Activate the plugin in Plugins → Installed Plugins.
- Add
[data-lean-cart]mount points to your theme templates. - Optionally set CSS custom properties to match your theme tokens.
No configuration page. No settings screen. It works on activation.
Usage
Disabling the default stylesheet
add_filter( 'lean_cart_disable_styles', '__return_true' );
Customizing i18n strings
add_filter( 'lean_cart_i18n', function( $strings ) {
$strings['emptyCart'] = __( 'Nothing here yet!', 'my-theme' );
return $strings;
} );
Extending the config object
add_filter( 'lean_cart_config', function( $config ) {
$config['myCustomKey'] = 'value';
return $config;
} );
Adding a custom module
PHP (includes/modules/class-my-plugin.php):
class Lean_Cart_My_Plugin {
public function __construct() {
add_filter( 'lean_cart_config', [ $this, 'extend_config' ] );
}
public function extend_config( array $config ): array {
$config['modules']['myPlugin'] = true;
return $config;
}
}
JS (assets/js/modules/my-plugin.js):
export function init( store ) {
store.addNormalizer( ( item, raw ) => {
// Enrich items based on Store API extension data.
return item;
} );
store.addFormExtractor( ( form ) => {
// Return extra fields for the add-to-cart API call.
return null;
} );
}
FAQ
Does this replace the WooCommerce cart page?
No. Lean Cart replaces the cart fragments system and the add-to-cart AJAX flow. The /cart/ page still works — Lean Cart operates alongside it, powering drawers, mini-carts, and any custom mount point.
Does it work with WooCommerce Blocks?
Lean Cart targets the same Store API that Blocks uses. They can coexist, but Lean Cart is designed for themes that want full control over cart markup rather than using the Cart Block.
What happens if JavaScript is disabled?
Forms fall through to their native POST behavior. The standard WooCommerce add-to-cart flow takes over and the /cart/ page works as usual.
Can I use this without any CSS?
Yes. Either dequeue the stylesheet with lean_cart_disable_styles or simply don't add the .lean-cart wrapper class and write your own styles targeting [data-lean-cart] attributes.
How do I add support for another WooCommerce extension?
Create a PHP module class in includes/modules/ that hooks into lean_cart_config, and a JS module in assets/js/modules/ that registers normalizers and/or form extractors. Gate the PHP module on class_exists() in Lean_Cart::load_modules(). See the Subscriptions or Mix and Match modules for reference.
Changelog
0.1.0
- Initial release.
- Store API cart state layer with optimistic updates and rollback.
- Native ES module architecture with import map cache busting.
- Skeleton CSS with CSS custom property token mapping.
- Cold-fetch guard — zero cart overhead for new visitors.
- Legacy cart fragments disabled automatically.
- Form interception for simple, variable, grouped, and subscription products.
- Loop/widget link interception for archive pages.
- Module: WooCommerce Subscriptions — billing summaries, badges, meta rows.
- Module: Mix and Match Products —
mnm_configform extraction, parent-child nesting, server-side validation guard. - Module: All Products for Subscriptions — Store API extension, purchase mode display.
- Event-driven theme integration via
lean-cart:*custom events. - Public JS API (
window.leanCart). data-lean-cartattribute-based mount point system.- Delegated click handler for cart actions (
data-cart-action). - Bridge to
window.numenunavigation system.
License
GPL-2.0-or-later