npm package: @fluid-app/fluid
Description: Fluid Commerce JavaScript SDK
Author: Fluid Commerce
Latest version: 1.1.5

# @fluid-app/fluid

The official JavaScript SDK for Fluid Commerce, providing easy access to cart management, checkout, and attribution tracking.

## Installation

```bash
npm install @fluid-app/fluid
# or
yarn add @fluid-app/fluid
# or
pnpm add @fluid-app/fluid
```

## Quick Start

```typescript
import {
  addCartItems,
  checkout,
  getCart,
  getCartItemCount,
  initializeFluid,
} from "@fluid-app/fluid";

// Initialize the SDK (typically in your app's entry point)
// Basic initialization with server-side attribution
initializeFluid({
  fluidShop: "your-shop-id", // Replace with your shop ID
});

// Optional: Initialize with attribution override
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    share_guid: "your-affiliate-id",
  },
});

// Examples of common operations:

// 1. Add a single item to cart
const cart1 = await addCartItems(12345);

// 2. Add a single item with quantity
const cart2 = await addCartItems(12345, { quantity: 3 });

// 3. Add multiple items to cart at once
const cart3 = await addCartItems([
  { variant_id: 12345, quantity: 1 },
  { variant_id: 67890, quantity: 2 },
]);

// 4. Get the current cart
const cart = await getCart();

// 5. Get the number of items in the cart
const itemCount = getCartItemCount();

// 6. Proceed to checkout (redirects user automatically)
await checkout();
```

## Quick Reference

### SDK Functions

- [initializeFluid](#initializefluid)
- [getCart](#getcart)
- [getCartItemCount](#getcartitemcount)
- [addCartItems](#addcartitems)
- [updateCartItems](#updatecartitems)
- [subscribeCartItem](#subscribecartitem)
- [unsubscribeCartItem](#unsubscribecartitem)
- [decrementCartItem](#decrementcartitem)
- [removeCartItemById](#removecartitembyid)
- [clearCart](#clearcart)
- [getLocalCart](#getlocalcart)
- [checkout](#checkout)
- [reset](#reset)
- [getAttribution](#getattribution)
- [lookupAffiliate](#lookupaffiliate)
- [updateLocaleSettings](#updatelocalesettings)

### Widgets

- [Cart Widget](#cart-widget)
- [Controlling the Cart](#controlling-the-cart)
- [Adding Products with Data Attributes](#adding-products-with-data-attributes)
- [Adding Enrollment Packs with Data Attributes](#adding-enrollment-packs-with-data-attributes)
- [Combined Enrollment Pack and Products](#combined-enrollment-pack-and-products)
- [Lead Capture Widget](#lead-capture-widget)

## Features

- **Cart Management**: Create carts, add/remove items, and update quantities
- **Checkout Flow**: One-click checkout with automatic redirect
- **Attribution Tracking**: Built-in tracking for affiliate marketing
- **Event Tracking**: Automatic tracking of page visits and cart interactions
- **Session Management**: Handles user sessions without cookies
- **Browser Fingerprinting**: Cross-session user identification
- **Optimistic Updates**: Updates UI immediately for better user experience
- **Typed API**: Full TypeScript support with accurate types

## Attribution Tracking

The Fluid SDK includes FairShare attribution tracking by default. When you initialize with `initializeFluid()`, attribution tracking is automatically set up with **server-side attribution calculation**.

### How Server-Side Attribution Works

1. **Automatic Attribution Calculation**: Attribution is now calculated server-side based on the current page URL when settings are fetched.

2. **Page Change Detection**: The SDK automatically detects URL changes (including SPA navigation) and refreshes attribution data as needed.

3. **Attribution Override Support**: You can override URL-based attribution with specific values for testing or fixed scenarios.

4. **Attribution Priority System**: Attribution override takes precedence over server-calculated attribution.

### Basic Server-Side Attribution

```typescript
// Basic initialization - attribution calculated server-side automatically
initializeFluid({
  fluidShop: "your-shop-id",
});
```

### Attribution Override

```typescript
// Attribution by email
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    email: "affiliate@example.com",
  },
});

// Attribution by username
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    username: "influencer-username",
  },
});

// Attribution by share GUID
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    share_guid: "abc123-def456-ghi789",
  },
});

// Attribution by Fluid rep ID
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    fluid_rep_id: 12345,
  },
});

// Attribution by external ID
initializeFluid({
  fluidShop: "your-shop-id",
  attributionOverride: {
    external_id: "external-affiliate-id",
  },
});
```

## API Reference

### Core Setup

#### initializeFluid

`initializeFluid(config)`

Initializes the Fluid SDK and the integrated FairShare tracking.

```typescript
import { initializeFluid } from "@fluid-app/fluid";

initializeFluid({
  fluidShop: "your-shop-id",
  debug: true, // Optional: enable debug logs
  apiEndpoint: "https://api.custom-endpoint.com", // Optional: custom API endpoint
});
```

You can provide a custom API endpoint for scenarios like local development, testing against staging, or connecting to a specific API instance.

#### getCart

`getCart()`

Gets the current cart data from the Fluid API.

```typescript
import { getCart } from "@fluid-app/fluid";

// Get the latest cart data from the API
const cart = await getCart();

// Access cart properties
if (cart?.items?.length > 0) {
  console.log(
    `Cart has ${cart.items.length} items with a total of $${cart.total}`,
  );
}
```

This makes a network request to fetch the most up-to-date cart information. For a faster, offline alternative that uses locally cached data, see [`getLocalCart()`](#getlocalcart).

### Cart Management

#### getCartItemCount

Gets the current number of items in the cart.

```typescript
import { getCartItemCount } from "@fluid-app/fluid";

// Get the current cart item count
const itemCount = getCartItemCount();
console.log(`Cart contains ${itemCount} items`);
```

#### addCartItems

Adds item(s) to the cart or increments their quantity if already in cart.

```typescript
// Add/increment a single item by 1
const cart1 = await addCartItems(12345);

// Add/increment a single item by 3
const cart2 = await addCartItems(12345, { quantity: 3 });

// Add/increment multiple items at once
const cart3 = await addCartItems([
  { variant_id: 12345, quantity: 2 },
  { variant_id: 67890, quantity: 1 },
]);

// Add an item with subscription
const cart4 = await addCartItems(12345, { subscribe: true });

// Add multiple items with mixed subscription preferences
const cart5 = await addCartItems([
  { variant_id: 12345, quantity: 1, subscribe: true },
  { variant_id: 67890, quantity: 2 },
]);
```

#### updateCartItems

Updates the quantities of multiple cart items at once, providing precise control over item quantities.

```typescript
import { updateCartItems } from "@fluid-app/fluid";

// Set exact quantities for multiple items
const cart = await updateCartItems([
  { variant_id: 12345, quantity: 3 },
  { variant_id: 67890, quantity: 1 },
]);

// This will set the quantities exactly as specified, regardless of previous values
```

#### subscribeCartItem

Sets a cart item to be purchased as a subscription.

```typescript
// Convert an existing cart item to a subscription
await subscribeCartItem(123); // where 123 is the item ID (not variant ID)
```

#### unsubscribeCartItem

Reverts a cart item from subscription to a one-time purchase.

```typescript
// Convert a subscription cart item back to a one-time purchase
await unsubscribeCartItem(123); // where 123 is the item ID (not variant ID)
```

#### decrementCartItem

Decrements the quantity of an item in the cart by the specified amount (or by 1 if no quantity is specified). If the quantity reaches 0, the item is removed from the cart.

```typescript
// Decrement item by 1 (removes if quantity becomes 0)
const cart1 = await decrementCartItem(12345);

// Decrement item by 2
const cart2 = await decrementCartItem(12345, { quantity: 2 });
```

#### removeCartItemById

Removes a cart item completely by its item ID (not variant ID). This function is useful for removing entire items regardless of quantity, such as enrollment packs.

```typescript
import { removeCartItemById } from "@fluid-app/fluid";

// Remove a cart item completely using its item ID
const cart = await removeCartItemById(itemId);

// This is particularly useful for enrollment packs
// where you want to remove the entire pack
const enrollmentItem = cart.items.find((item) => item.enrollment_pack_id);
if (enrollmentItem) {
  await removeCartItemById(enrollmentItem.id);
}
```

#### clearCart

Removes all items from the cart and deletes the cart session.

```typescript
clearCart();
```

#### getLocalCart

Gets the current cart directly from local storage without an API call.

```typescript
const cart = getLocalCart();
if (cart?.items?.length > 0) {
  console.log(`Cart has ${cart.items.length} items`);
}
```

### Displaying Cart Count in HTML

You can easily display the current cart count in any HTML element by giving it the ID `fluid-cart-count`. The SDK will automatically update this element whenever the cart changes.

```html
<!-- Simple cart count display -->
<span id="fluid-cart-count">0</span>
```

The cart count is updated automatically when:

- Items are added to the cart
- Items are removed from the cart
- Item quantities are updated
- The cart is cleared
- A new cart is created
- The cart is refreshed from the API

### Checkout

#### checkout

Initiates the checkout process. This:

1. Waits for any pending cart operations to complete
2. Includes attribution data if present
3. Redirects to the Fluid checkout page

```typescript
try {
  await checkout();
  // User is redirected automatically
} catch (error) {
  console.error("Checkout failed:", error);
}
```

### Utilities

#### reset

Resets the entire SDK state, including clearing the cart and all tracking data.

```typescript
import { reset } from "@fluid-app/fluid";

// Clear all data and reset state
reset();
```

#### getAttribution

Gets the current attribution data.

```typescript
import { getAttribution } from "@fluid-app/fluid";

const attribution = getAttribution();
if (attribution?.username) {
  console.log("Referred by username:", attribution.username);
} else if (attribution?.email) {
  console.log("Referred by email:", attribution.email);
} else if (attribution?.share_guid) {
  console.log("Referred by share GUID:", attribution.share_guid);
} else if (attribution?.fluid_rep_id) {
  console.log("Referred by Fluid rep ID:", attribution.fluid_rep_id);
} else if (attribution?.external_id) {
  console.log("Referred by external ID:", attribution.external_id);
}
```

#### lookupAffiliate

Looks up affiliate information by attribution data.

```typescript
import { lookupAffiliate } from "@fluid-app/fluid";

// Look up affiliate by username
try {
  const result = await lookupAffiliate({ username: "affiliate-username" });

  if (result?.affiliate) {
    console.log("Affiliate found:", result.affiliate.name);
    console.log("Email:", result.affiliate.email);
    console.log("Username:", result.affiliate.username);
    console.log("Share GUID:", result.affiliate.share_guid);
  }
} catch (error) {
  console.error("Affiliate not found or lookup failed:", error);
}

// Other lookup methods:
await lookupAffiliate({ email: "affiliate@example.com" });
await lookupAffiliate({ share_guid: "abc123" });
await lookupAffiliate({ fluid_rep_id: 123456 });
await lookupAffiliate({ external_id: "EXT-789" });
```

This function is useful for validating affiliate identifiers or retrieving affiliate information for display purposes.

#### updateLocaleSettings

Updates the locale settings for the cart, such as language and country.

```typescript
import { updateLocaleSettings } from "@fluid-app/fluid";

// Update language only
await updateLocaleSettings({ language: "fr" });

// Update country only
await updateLocaleSettings({ country: "CA" });

// Update both language and country
await updateLocaleSettings({
  language: "fr",
  country: "CA",
});
```

This is useful for multi-language/multi-region stores to ensure the cart uses the appropriate locale settings.

## Web Widgets

Fluid provides ready-to-use web components that can be easily integrated into any website. These widgets provide a streamlined shopping experience without requiring complex custom implementation.

### Installation

The widgets can be used via CDN or as an NPM package.

#### CDN / Script Tag Distribution

Use this method for direct inclusion in a website without a build step.

```html
<!-- Fluid SDK script tag with configuration attributes -->
<script
  id="fluid-cdn-script"
  src="https://assets.fluid.app/scripts/fluid-sdk/latest/web-widgets/index.js"
  data-fluid-shop="your-shop-id"
  data-fluid-api-base-url="https://api.your-custom-endpoint.com"
></script>
<!-- ^ The data-fluid-api-base-url attribute specifies a custom API endpoint -->
<!-- Attribution is now handled automatically server-side based on the page URL -->

<!-- Add the cart widget anywhere on your page -->
<fluid-cart-widget
  position="bottom-right"
  fluid-shop-display-name="Your Store Name"
></fluid-cart-widget>
```

#### Key Benefits of CDN Distribution

- **No NPM installation required** - Works with any HTML page
- **Automatic style injection** - No separate CSS file to include
- **Single script tag does everything** - Initializes SDK, loads styles, registers components
- **Flexibility through data attributes**:
  - `data-fluid-shop` - Your shop ID (required)
  - `data-fluid-api-base-url` - Use custom API endpoint (for development/staging)
  - `data-share-guid` - Set hard-coded attribution (optional)
  - `data-debug` - Enable debug mode

#### NPM Installation

```bash
npm install @fluid-app/web-widgets
```

```javascript
// Import the components and styles
import { initializeFluid } from "@fluid-app/fluid";
import { registerWebComponents } from "@fluid-app/web-widgets";

import "@fluid-app/web-widgets/styles.css";

// Initialize Fluid SDK with your shop ID
initializeFluid({
  fluidShop: "your-shop-id",
});

// Register the web components
registerWebComponents();
```

### Available Widgets

#### Cart Widget

The cart widget provides a floating cart button and drawer interface for your website.

```html
<!-- Basic cart widget -->
<fluid-cart-widget
  position="bottom-right"
  fluid-shop-display-name="Your Store"
></fluid-cart-widget>
```

**Attributes:**

| Attribute               | Type    | Default        | Description                                                                |
| ----------------------- | ------- | -------------- | -------------------------------------------------------------------------- |
| position                | string  | 'bottom-right' | Position on screen: 'top-left', 'top-right', 'bottom-left', 'bottom-right' |
| fluid-shop-display-name | string  | 'My Store'     | Shop name displayed in the cart header                                     |
| size                    | number  | 60             | Size of the widget button in pixels                                        |
| x-margin                | number  | 40             | Horizontal margin from the edge in pixels                                  |
| y-margin                | number  | 98             | Vertical margin from the edge in pixels                                    |
| z-index                 | number  | 50             | Z-index for layering                                                       |
| hide-widget             | boolean | false          | When true, hides button but keeps cart functionality                       |

#### Controlling the Cart

You can control the cart using data attributes:

```html
<!-- Cart control buttons -->
<button data-fluid-cart="open">Open Cart</button>
<button data-fluid-cart="close">Close Cart</button>
<button data-fluid-cart="toggle">Toggle Cart</button>
```

Or using JavaScript:

```javascript
// When using NPM package
import { closeCart, openCart, toggleCart } from "@fluid-app/react-widgets";

openCart();
closeCart();
toggleCart();

// When using CDN
window.fluidCart.open();
window.fluidCart.close();
window.fluidCart.toggle();
```

#### Adding Products with Data Attributes

Data attributes provide a simple way to add products to the cart without writing JavaScript:

```html
<!-- Basic usage with default quantity (1) - cart opens automatically -->
<button data-fluid-add-to-cart="22190">Add to Cart</button>

<!-- With specific quantity - cart opens automatically -->
<button data-fluid-add-to-cart="22190" data-fluid-quantity="2">
  Add 2 to Cart
</button>

<!-- Add to cart without opening cart drawer immediately -->
<button data-fluid-add-to-cart="22190" data-fluid-open-cart-after-add="false">
  Add to Cart (Keep Shopping)
</button>

<!-- Add with subscription enabled -->
<button data-fluid-add-to-cart="22190" data-fluid-subscribe="true">
  Add with Subscription
</button>

<!-- Add multiple products with one subscribed -->
<button data-fluid-add-to-cart="22190,22191" data-fluid-subscribe="true,false">
  Add Multiple (First Subscribed)
</button>
```

#### Adding Enrollment Packs with Data Attributes

Enrollment packs can be added using data attributes:

```html
<!-- Basic usage to add an enrollment pack - cart opens automatically -->
<button data-fluid-add-enrollment-pack="64">Add Enrollment Pack</button>

<!-- Add enrollment pack without opening cart drawer immediately -->
<button
  data-fluid-add-enrollment-pack="64"
  data-fluid-open-cart-after-add="false"
>
  Add Enrollment Pack (Keep Shopping)
</button>
```

#### Combined Enrollment Pack and Products

You can combine enrollment packs and regular products in a single action:

```html
<!-- Add enrollment pack and product together -->
<button data-fluid-add-enrollment-pack="64" data-fluid-add-to-cart="22190">
  Add Enrollment Pack with Product
</button>

<!-- Add multiple products with the enrollment pack -->
<button
  data-fluid-add-enrollment-pack="64"
  data-fluid-add-to-cart="22190,22191"
>
  Add Enrollment Pack with Multiple Products
</button>

<!-- With subscription enabled for the products -->
<button
  data-fluid-add-enrollment-pack="64"
  data-fluid-add-to-cart="22190,22191"
  data-fluid-subscribe="true"
>
  Add Pack with Subscribed Products
</button>

<!-- Different subscription settings for each product -->
<button
  data-fluid-add-enrollment-pack="64"
  data-fluid-add-to-cart="22190,22191"
  data-fluid-subscribe="true,false"
>
  Add Pack with Mixed Subscription Products
</button>
```

#### Lead Capture Widget

The lead capture widget provides a form for collecting customer information.

```html
<!-- Basic lead capture widget -->
<fluid-lead-capture-widget
  title="Get Updates"
  contact-method="phone"
></fluid-lead-capture-widget>
```

### Customizing Widget Styles

You can customize the widgets to match your brand using CSS variables:

```css
:root {
  --company-color: #4361ee; /* Your brand color */
  --ff-body: "Your Custom Font", sans-serif; /* Your custom font */
}
```

- `--company-color`: Sets the primary color for buttons, accents, and highlights
- `--ff-body`: Sets the font family used throughout the widgets (falls back to system fonts if not specified)

This allows you to maintain consistent branding and typography across your website and the Fluid widgets.

## Debugging

Enable debug mode to see detailed logs:

```typescript
initializeFluid({
  fluidShop: "your-shop-id",
  debug: true,
});
```

You can also inspect the localStorage to see what data is being stored:

- `fs_cart`: Cart data
- `fs_attribution`: Attribution data
- `fs_session`: Session token

## License

MIT