data-* attributes directly to your HTML elements. Both approaches work together, so you can mix declarative HTML for simple cases and the JavaScript API for anything more complex.
Declarative HTML Controls
For common actions you don’t need to write any JavaScript. Add the followingdata-* attributes to any HTML element and FairShare handles the rest.
Opening and closing the cart panel:
data-fluid-add-to-cart to the variant ID:
data-fluid-subscription-plan-id takes a single plan ID. data-fluid-bundled-items and data-fluid-bundle-selections take JSON; the SDK logs invalid JSON to the console and adds the item without it. Attribute names are case-sensitive kebab-case: data-fluid-addToCart does nothing.
Cart Display
Place a<span> with id="fluid-cart-count" anywhere in your markup and FairShare will keep its text content in sync with the current cart item count. The count updates automatically when items are added, removed, updated, or when the cart is cleared, created, or refreshed — no JavaScript needed.
Real-time cart sync
Real-time cart updates arrive over Action Cable, Fluid’s WebSocket transport. The SDK applies each update to local cart state and notifies mounted cart widgets. Your existing cart methods stay the same. The loader on every Fluid storefront always setsdata-realtime-enabled="true", so you don’t turn it on in a theme. If you install the SDK by hand outside a Fluid theme, add the attribute yourself. See Enable real-time cart updates.
Carts that fill in the background
Every Fluid storefront creates carts in the background. The first add on a new cart creates the cart, and the server fills it in. The cart’spopulation_status moves from pending to complete, or to failed if the items cannot be added. The fill usually takes a few hundred milliseconds to about 2 seconds.
The cart methods wait for the fill, so you rarely handle pending yourself:
CART_OPERATION_SUCCESS carries the filled cart too. When a fill fails, the add resolves undefined, no success event fires, and the SDK emits CART_POPULATION_FAILED with the reason.
Use whenCartSettled() when your code needs the items and does not hold the value an add resolved with. It takes an optional timeoutMs, which defaults to 10 seconds. It resolves with the filled cart, a failed cart (population_status: "failed"), or a cart that is still pending when the wait ends. It resolves null when there is no cart, and it never rejects.
JavaScript Cart Methods
All cart methods live onwindow.FairShareSDK. Async methods return Promises — use await or .then() depending on your environment.
The REST-backed cart methods here call the Fluid Public SDK API
(public-v2025-06) — the surface the SDK is generated against. They do not call
the direct Checkout API that the headless checkout
guide teaches. Both surfaces read and write the
same cart records, but their request shapes differ, so drive any one cart
through one surface only. See Choosing a cart
surface if you also call REST directly.
The operation behind each method
Each REST-backed method maps to the Public SDK API operation or operations it calls. Follow a link for the full request and response contract.Reading Cart State
getCart() fetches the cart. refreshCart() also dispatches the cart-update
event that tells mounted widgets to render the fresh state.
To adopt a cart created outside the SDK, validate and store its token:
setCartToken() updates local state, mounted widgets, and any configured
real-time cart subscription after validation succeeds. It rejects an empty,
unavailable, or completed token. Validation and fetch failures that happen
before adoption leave the existing local cart intact. Some later failures can
reject after local state has changed, so inspect the current cart in your error
handler. Only one adoption can run at a time.
The CDN loader also recognizes a cart_token query parameter. After a
successful adoption, it removes that parameter with history.replaceState.
Modifying the Cart
quantity to 1. In the array form, each parent
item includes variant_id and quantity. Use subscribe and
subscription_plan_id for a parent subscription.
For a bundle, put the selected children in bundled_items. Each child includes
variant_id and quantity, and can include product_bundle_group_id,
subscription, and subscription_plan_id. The direct checkout Add Items
contract models all five child fields.
The SDK forwards nested child fields at runtime, but its current public
TypeScript declaration may omit product_bundle_group_id. JavaScript callers
can send it. TypeScript callers may need a local type extension until the SDK
declaration catches up. This SDK method uses the FairShare cart lifecycle — it
posts to Adds items to cart on the
Public SDK API, not to the Add Items operation in the generated Checkout
endpoints reference.
clearCart() resets the local client state. It does not call a dedicated
server-side clear-cart operation.
Subscriptions
Enrollment Packs
Enrollment packs bundle products for new rep onboarding. Add a pack by its numeric ID:bundleSelections:
bundleSelections is the SDK option name. The nested bundled_items array
describes the selected items within each parent variant.
Checkout
getCheckoutUrl() returns undefined until a cart token exists.
To replace the default checkout behavior, register a handler:
Cart Control
You can open, close, and toggle the cart panel from JavaScript in addition to the declarative HTML attributes described above:The legacy Prefer
window.fluidCart namespace is also supported for backwards compatibility:window.FairShareSDK.cart.control for new integrations.Cart Settings
Cart settings let you override the default widget appearance and position on a per-page basis. Settings are page-scoped — they apply to the current URL path and are stored separately for each path, excluding query parameters and URL fragments. This means you can display the cart widget in the bottom-right corner on your homepage while hiding it entirely on a custom checkout page. Setting cart options:<fluid-cart-widget> element):
Shop Settings
Read and update shop-level configuration, including locale and language preferences.Cart state events
The SDK listens forcart_update messages over Action Cable when real-time sync is on. It is always on in a Fluid storefront, and you set it only on a hand-installed tag. After applying a received cart, it emits a CustomEvent named UPDATE_CART_ITEM on window. Built-in cart widgets already listen for this event; add a listener when you build your own cart display.
The WebSocket message name cart_update is not a browser event name. Listen for UPDATE_CART_ITEM on window to react to the applied update. Other WebSocket message types are ignored by the SDK’s cart-sync subscription.
These browser events also help you keep custom cart displays in sync with local SDK actions:
CART_ITEMS_ADDING and CART_ITEMS_SETTLED bracket every add of items, bundles and enrollment packs, and each pair shares an operationId. The detail of CART_ITEMS_ADDING holds operationId, cartToken (null on the add that creates the cart), lines, and enrollment when the add claims an enrollment pack. The detail of CART_ITEMS_SETTLED holds operationId, outcome ("success" or "error"), cartToken, and the cart the add resolved with, when there is one. That cart can still be filling. Code that needs the final items should use the value the add resolves with, whenCartSettled(), or onCartChange(). See Build a custom cart drawer.
State events can describe optimistic changes. Use cart operation events when you need a mutation’s success or failure rather than a display update. A received real-time update does not by itself emit CART_OPERATION_SUCCESS or CART_OPERATION_ERROR.
id="cart-summary-count". You can also use the built-in fluid-cart-count element described under cart display.
State-event details can include cart, handledByWidget, and widgetId. SDK-dispatched cart events also include a unique eventId and an ISO timestamp. Treat widgetId as a producer identifier, not as a transport selector: Action Cable updates retain the historical widgetId: "pusher" value. Use eventId if your integration needs to deduplicate the same dispatch.
For loading indicators around widget actions, listen for CART_LOADING_START and CART_LOADING_END on window. These are widget loading signals, not a success or failure result for every SDK method.
Cart operation events
These seven methods emit a consistent success or error event onwindow:
addCartItems()addEnrollmentPack()setEnrollment()updateCartItems()decrementCartItem()removeCartItemById()updateCartItemVariant()
operation, the add / update / remove category,
the per-call toast preference, identifying IDs when available, and an ISO
timestamp. A success can include the updated cart. For addCartItems(),
addEnrollmentPack() and setEnrollment(), that is the filled cart. An error
includes the raw thrown error and a human-readable message.
When an error reaches the public operation wrapper, all seven methods emit
CART_OPERATION_ERROR. addCartItems() and decrementCartItem() then resolve
undefined to preserve their historical contract. The other five methods
rethrow the error. A no-op result from addCartItems(), addEnrollmentPack(),
setEnrollment(), decrementCartItem(), or updateCartItemVariant() emits no
success event. So does a fill that fails.
updateCartItems() emits success when its void operation completes.
Cart population failures
When the server cannot fill a new cart, the cart stays empty. For example, none of the items may be sold in the shopper’s country. The call that created the cart resolvesundefined, and no CART_OPERATION_SUCCESS fires. The SDK emits CART_POPULATION_FAILED on window for the cart it currently holds:
id="cart-notice".
The event relies on real-time sync, which is always on in a Fluid storefront. The SDK leaves the empty cart in place, so the shopper can add to it again. A failure for a cart the SDK has since cleared or replaced is ignored.
When the built-in toast shows errors, it also shows this event. It uses
apiMessage when present and the error message otherwise. Your error message override replaces both. See cart feedback.
Analytics
Fire events and inspect attribution state directly from your JavaScript code.Lead Capture
Capture a prospect’s contact information and send it to Fluid without requiring the lead capture widget. This is useful when you’re building a custom form or capturing leads as part of another flow.contact — name, email, and phone. Keys outside that shape are dropped silently, so a flat payload submits an empty lead and still resolves successfully. Every field is optional, but send at least one of email or phone so the lead is reachable. The lead is attributed to the same rep resolved at SDK initialization.