npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

alpinejs-shopify-cart

v1.0.0

Published

A reactive Alpine.js cart magic powered by Shopify standard storefront events and actions

Readme

alpinejs-shopify-cart

A headless, reactive $cart magic for Alpine.js, powered by Shopify's standard storefront events and actions.

npm · Documentation · Demo

What it does

  • Reads the cart with Shopify.actions.getCart().
  • Adds, updates, and removes lines through Shopify.actions.updateCart().
  • Updates cart notes, attributes, and discount codes.
  • Opens the theme's cart through Shopify.actions.openCart().
  • Observes standard shopify:cart:* events from other theme or app code.
  • Shares reactive state across Alpine components, with queued local operations and separate errors, user errors, and warnings.

The plugin does not provide markup, CSS, a cart drawer, analytics, or an Ajax Cart API fallback. It targets Shopify Liquid storefronts, not Admin apps, checkout extensions, POS, or headless storefronts. No API token is needed.

Installation

Choose one approach. Load Alpine only once; if your theme already initializes it, register this plugin in that existing setup rather than adding another Alpine script or calling Alpine.start() again.

npm

npm install alpinejs@^3 [email protected]

Register the plugin before starting Alpine:

import Alpine from 'alpinejs'
import shopifyCart from 'alpinejs-shopify-cart'

Alpine.plugin(shopifyCart)
Alpine.start()

The package provides ES module and CommonJS entries and TypeScript declarations. Module imports do not auto-register.

CDN

Load the plugin's auto-registering CDN build before Alpine. Both scripts should use defer:

<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/alpinejs-shopify-cart.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>

Plugin on jsDelivr · Package on npm

Serve the files as theme assets

If you prefer Shopify-hosted assets, copy node_modules/alpinejs-shopify-cart/dist/alpinejs-shopify-cart.min.js into your theme's assets/ directory. Copy node_modules/alpinejs/dist/cdn.min.js there as alpine.js, then load them in the same order:

<script defer src="{{ 'alpinejs-shopify-cart.min.js' | asset_url }}"></script>
<script defer src="{{ 'alpine.js' | asset_url }}"></script>

Your theme must provide Shopify's standard storefront actions. This does not turn a standalone HTML page into a Shopify storefront.

Reactive state

All $cart references resolve to Alpine.store('shopifyCart').

<div x-data>
    <span x-text="$cart.totalQuantity"></span>
    <span x-show="$cart.pending">Updating cart…</span>
</div>

| Property | Meaning | | --- | --- | | ready | An initial read attempt completed, including failure. Check error separately. | | cart | Raw Shopify cart result, or null. | | lines | Line array, normalized from an array or a { nodes } connection. | | totalQuantity | Quantity across all lines, or zero. | | cost | Cart cost, or null. Amounts are decimal strings, not integer cents. | | discountCodes | Codes and their applicability, as returned by Shopify. | | note, attributes | Last successfully observed values; undefined until known. Cleared when cart identity changes. | | pending, pendingCount | Whether operations remain, and how many are tracked, including queued calls. | | pendingOperation | Most recently registered pending operation, or null. | | error | Rejected operation details, or null. | | userErrors, warnings | Resolved validation errors and non-blocking warnings. | | detail | Custom detail from the applied result. |

The standard cart summary omits product titles, images, and full merchandise objects. Render those through Liquid or a separate product-data source.

Methods

Mutation methods resolve with the complete Shopify result, including cart, userErrors, and warnings. A failed request rejects its promise; callers must catch it.

await cart.add({ merchandiseId: variantId, quantity: 1 })
await cart.update({ id: line.id, quantity: 2 })
await cart.remove(line.id)
await cart.setNote('Leave at the front desk')
await cart.setAttributes([{ key: 'Gift wrap', value: 'Yes' }])
await cart.setDiscountCodes(['WELCOME10'])
await cart.refresh()
await cart.open()

Here cart is obtained from Alpine.store('shopifyCart'), or as this.$cart in an Alpine component.

  • add() and update() accept one line or an array. remove() accepts one line ID or an array.
  • Use merchandiseId to add a variant; use a returned cart line id to update or remove it.
  • Attributes and discount codes are complete replacement sets. An empty array clears the set.
  • mutate(payload, options) accepts a complete standard updateCart payload.
  • refresh({ cartId?, signal? }) returns { cart }.
  • open() takes no arguments and returns Promise<void>.

Mutation options accept signal, context, detail, and standard nested event options. Top-level context and detail take precedence over nested values. The plugin reserves event.detail.alpineShopifyCartOperationId for correlation.

await cart.mutate({
    lines: [{ id: line.id, quantity: 2 }],
    note: 'Gift order',
}, {
    context: 'cart',
    detail: { source: 'cart-page' },
})

Quantities are absolute targets. When deriving a new quantity from current state, disable controls while pending to avoid repeatedly submitting the same target.

For complete examples, see line-item properties and a gift-wrap form and cart recipes, including multi-item adds, selling plans, notes, attributes, and discounts.

Errors and warnings

try {
    const result = await cart.add({ merchandiseId: variantId, quantity: 1 })
    if (result.userErrors?.length) {
        showMessage(result.userErrors[0].message)
        return
    }
    if (result.warnings?.length) showMessage(result.warnings[0].message)
    await cart.open()
} catch (error) {
    showMessage(error.message ?? 'Could not update the cart. Try again.')
}

showMessage represents your theme's UI. A resolved userErrors array means Shopify declined a change; a warning means it applied with an adjustment. Neither should be treated as a network failure.

Events and theme behavior

The plugin listens on document for:

shopify:cart:lines-update
shopify:cart:note-update
shopify:cart:attributes-update
shopify:cart:discount-update
shopify:cart:error
shopify:cart:view

Mutation events carry a result promise. Their properties are directly on the event, not all inside event.detail. The plugin waits for the result and reconciles the cart. Events belonging to its own actions are consumed without duplicate reconciliation.

Shopify.actions.updateCart() already emits standard events. Do not emit another event after calling $cart. Changes outside standard actions are observable only when the theme or app dispatches standard events; the plugin does not intercept fetch or poll.

Shopify's default action can reload the page. For in-place updates, your theme must configure the appropriate updateCart handler and event target. openCart may open a drawer or navigate to the cart page. The plugin respects those decisions and does not configure actions itself.

Standard events are not a consent-aware analytics channel. Use Shopify Web Pixels for analytics.

Initialization and cleanup

Register before Alpine.start(). The plugin attaches listeners immediately and defers its initial read while the document and Shopify action runtime initialize. An unavailable runtime is recorded in error; refresh() can retry later.

The store and listeners belong to the page. Do not dispose when just one widget is removed. At application teardown:

Alpine.store('shopifyCart').dispose()

Disposal removes listeners, cancels initialization, rejects new calls, skips queued calls that have not started, and prevents late results from writing reactive state. It does not abort a request already sent or undo a server-side mutation. There is no restart API.

Local requests are serialized, and stale responses do not replace newer successful cart snapshots. A newer failed operation does not discard an earlier successful cart result. This is not distributed concurrency control: unrelated writers and events without a server revision still require integration testing.

Development and validation

Requires Node.js 20 or newer for the package; Node.js 24 for the site.

git clone https://github.com/BillyNoyes/alpinejs-shopify-cart.git
cd alpinejs-shopify-cart
npm ci
npx playwright install chromium firefox webkit
npm test

npm --prefix site ci
npm --prefix site run check

Tests cover lifecycle and race regressions, packed ESM/CommonJS consumers, CDN tree-shaking, public types, and browser integration with standard and CSP Alpine builds. The browser runtime is a controlled test double, not a live Shopify store. Live-store validation requires separate authorization and a real development theme.

The source is split into registration (index.js), Shopify transport (actions.js), event observation (events.js), operation ordering and reconciliation (operations.js), and reactive state/methods (store.js).

See the site guide, the development plan, and the Shopify contract.

Publishing

Maintainers: follow the release guide for first-package bootstrapping, npm trusted publishing, and tagged releases. Pushing to main does not publish the package.

References

License

MIT. Independent project by Billy Noyes, with no Shopify sponsorship or endorsement.