> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stigg.io/llms.txt
> Use this file to discover all available pages before exploring further.

# React

## Overview

The Stigg React SDK is a Javascript library for implementing pricing and packaging in React apps with Stigg. It provides plug-and-play components and custom React hooks. It's built on top of the JavaScript SDK and is the recommended method for integrating Stigg into your React application.

<Note>
  `@stigg/react-sdk` bundles the JavaScript SDK — you do not need to install `@stigg/js-client-sdk` separately.
</Note>

## Installing the SDK

You have a few options for using the `@stigg/react-sdk` package in your project:

From npm:

<CodeGroup>
  ```shell npm theme={null}
  npm install @stigg/react-sdk
  ```

  ```Text Yarn theme={null}
  yarn add @stigg/react-sdk
  ```
</CodeGroup>

## Retrieving the publishable key

In the Stigg app, go to **Integrations > API keys**.

Copy the **Publishable key** of the relevant environment.

## Getting started

First, you'll need to wrap your application in a single `StiggProvider` component. that will provide the React Context to components that are placed inside your application:

<CodeGroup>
  ```javascript React theme={null}
  import { StiggProvider } from '@stigg/react-sdk';

  export function App() {
    return (
      <StiggProvider apiKey="<STIGG_PUBLISHABLE_KEY>">
        <NestedComponents />
      </StiggProvider>
    );
  }
  ```
</CodeGroup>

### Importing the styles

If you plan to use the widget components, add an import statement to include the bundled styles:

<CodeGroup>
  ```typescript TypeScript theme={null}
  import '@stigg/react-sdk/dist/styles.css';
  ```
</CodeGroup>

### Setting the customer context

For in-app use (paywalls, customer portal, entitlement checks), pass the signed-in customer's ID directly to `StiggProvider`. This is the recommended approach — you do not need to use the JavaScript SDK separately to set the customer ID:

<CodeGroup>
  ```javascript React theme={null}
  import { StiggProvider } from '@stigg/react-sdk';

  export function App() {    
    return (
      <StiggProvider apiKey="<STIGG_PUBLISHABLE_KEY>" customerId={user.id}>
        <NestedComponents />
      </StiggProvider>
    );
  }
  ```
</CodeGroup>

If the `customerId` is not yet available when `StiggProvider` first renders (for example, it resolves after an async auth call), you can set it later via `setCustomerId` from the `useStiggContext` hook:

<CodeGroup>
  ```javascript React theme={null}
  import { useStiggContext } from '@stigg/react-sdk';

  export function App() { 
    const { stigg } = useStiggContext();
    
    useEffect(() => {
      stigg.setCustomerId(user.id);
    }, [stigg]);
  }
  ```
</CodeGroup>

## Rendering widgets

<Card title="Pricing table" icon="code-simple" horizontal href="/documentation/snap-in-widgets/pricing-table">
  Allow customers to select the plan that that they'd like to subscribe to from your public pricing page or in-app paywall
</Card>

<Card title="Customer portal" icon="code-simple" horizontal href="/documentation/snap-in-widgets/customer-portal">
  Introduce self-service and drive in-app expansions using only a few lines of codes
</Card>

<Card title="Checkout" icon="code-simple" horizontal="false" href="/documentation/snap-in-widgets/checkout">
  Easily accept payments without ever worrying about changes to your pricing model or migration to another billing provider
</Card>

## Custom hooks

The React SDK provides hooks that give you access to the Stigg object, entitlement checks, and helper methods for fetching data:

* [useStiggContext](./react#usestiggcontext)
* [useBooleanEntitlement](./react#usebooleanentitlement)
* [useNumericEntitlement](./react#usenumericentitlement)
* [useMeteredEntitlement](./react#usemeteredentitlement)
* [useCreditEntitlement](./react#usecreditentitlement)
* [useActiveSubscriptions](./react#useactivesubscriptions)
* [useCustomerPortal](./react#usecustomerportal)
* [usePaywall](./react#usePaywall)

### useStiggContext

Use the `useStiggContext` React hook to access [Stigg's JavaScript client](./javascript) easily from every component:

<CodeGroup>
  ```typescript React theme={null}
  import React from 'react';  
  import { useStiggContext } from '@stigg/react-sdk';

  function App() {  
    const { stigg } = useStiggContext();

    useEffect(() => {  
      // Use stigg Javascript client function  
      stigg.getBooleanEntitlement(...)  
    })  
  }
  ```
</CodeGroup>

### useBooleanEntitlement

The `useBooleanEntitlement` allows checking access for boolean entitlement:

<CodeGroup>
  ```typescript React theme={null}
  import { useBooleanEntitlement, BooleanEntitlementFallback } from '@stigg/react-sdk';

  const ENTITLEMENT_FALLBACK: BooleanEntitlementFallback = {
    hasAccess: true,
  };

  function App() {  
    const entitlement = useBooleanEntitlement({ 
      featureId: 'feature-custom-domain',
      options: { fallback: ENTITLEMENT_FALLBACK }
    });

    if (!entitlement.hasAccess) {
      return null
    }

    return <div>Customer has access to custom domain</div>
  }
  ```
</CodeGroup>

### useNumericEntitlement

The `useNumericEntitlement` allows checking access for numeric entitlement.

<Note>
  Use `useNumericEntitlement` for **non-metered numeric features** only. For metered features, use [`useMeteredEntitlement`](#usemeteredentitlement) instead — calling `useNumericEntitlement` on a metered feature returns a `FeatureTypeMismatch` fallback in v9.0.0+.
</Note>

<CodeGroup>
  ```typescript React theme={null}
  import { useNumericEntitlement, NumericEntitlementFallback } from '@stigg/react-sdk';

  const ENTITLEMENT_FALLBACK: NumericEntitlementFallback = {
    hasAccess: true,
    value: 100
  };

  function App() {  
    const entitlement = useNumericEntitlement({ 
      featureId: 'feature-max-file-size',
      options: { fallback: ENTITLEMENT_FALLBACK }
    });

    if (!entitlement.hasAccess) {
      return null
    }

    return <div>Customer has access to file size of {entitlement.value}GB</div>
  }
  ```
</CodeGroup>

### useMeteredEntitlement

The `useMeteredEntitlement` allows checking access for metered entitlement:

<CodeGroup>
  ```typescript React theme={null}
  import { useMeteredEntitlement, MeteredEntitlementFallback } from '@stigg/react-sdk';

  const ENTITLEMENT_FALLBACK: MeteredEntitlementFallback = {
    hasAccess: true,
  	isUnlimited: true,
  };

  function App() {  
    const entitlement = useMeteredEntitlement({ 
      featureId: 'feature-active-users',
      options: { 
        requestedUsage: 1,
        fallback: ENTITLEMENT_FALLBACK 
      }
    });

    if (!entitlement.hasAccess) {
      return null
    }

    return <div>Active users: {entitlement.currentUsage} / {entitlement.usageLimit}</div>
  }
  ```
</CodeGroup>

### useCreditEntitlement

The `useCreditEntitlement` hook returns the credit entitlement for a given credit-type feature. It reads directly from the SDK's local cache — no extra API call, no rate-limit concerns. Available since `@stigg/react-sdk` v8.0.0.

<CodeGroup>
  ```typescript React theme={null}
  import { useCreditEntitlement, CreditEntitlementFallback } from '@stigg/react-sdk';

  const ENTITLEMENT_FALLBACK: CreditEntitlementFallback = {
    hasAccess: true,
    usageLimit: 0,
    currentUsage: 0,
  };

  function App() {
    const entitlement = useCreditEntitlement({
      currencyId: 'feature-ai-tokens',
      options: { fallback: ENTITLEMENT_FALLBACK }
    });

    if (!entitlement.hasAccess) {
      return null
    }

    return (
      <div>
        Usage: {entitlement.currentUsage} / {entitlement.usageLimit}
      </div>
    )
  }
  ```
</CodeGroup>

### useActiveSubscriptions

The `useActiveSubscriptions` return the active subscriptions list:

<CodeGroup>
  ```typescript React theme={null}
  import { useActiveSubscriptions } from '@stigg/react-sdk';

  function App() {  
    const { activeSubscriptions, isLoaded } = useActiveSubscriptions();

    if (!isLoaded) {
      // Handle loading state
      return null
    }

    return (
      <div>You have {activeSubscriptions.length} active subscriptions</div>
    )
  }
  ```
</CodeGroup>

### useCustomerPortal

The `useCustomerPortal` return the customer portal object:

<CodeGroup>
  ```typescript React theme={null}
  import { useCustomerPortal } from '@stigg/react-sdk';

  function App() {  
    const { customerPortal, isLoaded } = useCustomerPortal();

    if (!isLoaded) {
      // Handle loading state
      return null
    }

    return (
      <div>You have {customerPortal.subscriptions} subscriptions</div>
    )
  }
  ```
</CodeGroup>

### usePaywall

The `usePaywall` return the paywall object:

<CodeGroup>
  ```typescript React theme={null}
  import { usePaywall } from '@stigg/react-sdk';

  function App() {  
    const { paywall, isLoaded } = usePaywall();

    if (!isLoaded) {
      // Handle loading state
      return null
    }

    return (
      <div>You can subscribe to the following plan: {paywall.plans}</div>
    )
  }
  ```
</CodeGroup>

## Entitlement guard components

Entitlement guard components are useful in the cases where you need to wrap some part of the UI with an entitlement check and show a component in case the customer has no access to the feature:

### BooleanEntitlementGuard

<CodeGroup>
  ```javascript React theme={null}
  import { BooleanEntitlementGuard } from '@stigg/react-sdk';

  function App() {  
    return (
      <BooleanEntitlementGuard 
        featureId="feature-custom-domain"
        noAccessComponent={<NoCustomDomain />}
        options={{ fallback: { hasAccess: true } }}>
        <CustomDomainSetup />
      </BooleanEntitlementGuard>
    )
  }
  ```
</CodeGroup>

### NumericEntitlementGuard

<CodeGroup>
  ```javascript React theme={null}
  import { NumericEntitlementGuard } from '@stigg/react-sdk';

  function App() {  
    return (
      <NumericEntitlementGuard 
        featureId="feature-max-file-size"
        noAccessComponent={<NoFileUpload />}
        options={{ fallback: { hasAccess: true, value: 100 } }}>
        <FileUpload />
      </NumericEntitlementGuard>
    )
  }
  ```
</CodeGroup>

### MeteredEntitlementGuard

<CodeGroup>
  ```javascript React theme={null}
  import { MeteredEntitlementGuard } from '@stigg/react-sdk';

  function App() {  
    return (
      <MeteredEntitlementGuard 
        featureId="feature-active-users"
        noAccessComponent={<NoAccess />}
        options={{ fallback: { hasAccess: true, isUnlimited: true } }}>
        <MainApp />
      </MeteredEntitlementGuard>
    )
  }
  ```
</CodeGroup>

## Checkout widget callbacks

The Checkout widget supports user interaction callbacks that let you validate input, block navigation between steps, and show custom errors. All callbacks are optional and can be async.

### Callback signatures

<CodeGroup>
  ```ts TypeScript theme={null}
  import {
    Addon,
    BillableFeature,
    // CheckoutStep,
    // CheckoutState,
  } from '@stigg/react-sdk';

  // Validate billable feature quantity changes (e.g., seats)
  onBillableFeatureChange?: (args: {
    feature: BillableFeature;
    quantity: number;
  }) => Promise<{ errorMessage?: string; disableNextStep?: boolean }>;

  // Validate addon selection or quantity changes
  onAddonChange?: (args: {
    addon: Addon;
    quantity: number;
  }) => Promise<{ errorMessage?: string; disableNextStep?: boolean }>;

  // Run checks before moving between steps; return false to block navigation
  onBeforeStepChange?: (args: {
    // TODO: replace with exported CheckoutStep when available
    from: 'PLAN' | 'ADDONS' | 'PAYMENT';
    to: 'PLAN' | 'ADDONS' | 'PAYMENT';
    // TODO: replace with exported CheckoutState when available
    state: unknown; // current checkout snapshot (plan, addons, features, quantities, etc.)
  }) => Promise<boolean>;
  ```
</CodeGroup>

#### `onBillableFeatureChange`

Runs whenever a billable feature quantity is adjusted (e.g., seats, API calls). Return `{ errorMessage, disableNextStep }` to surface inline validation and/or prevent advancing.

<CodeGroup>
  ```ts TypeScript theme={null}
  import { BillableFeature } from '@stigg/react-sdk';

  onBillableFeatureChange={async ({ feature, quantity }) => {
  const validation = // some asynchronous validation
          if (validation) {
            return { errorMessage: `Validation failed for feature: ${feature.displayName}`, disableNextStep: true };
          }
          return { disableNextStep: false };
        }}
  ```
</CodeGroup>

#### `onAddonChange`

Fires when an addon is added/removed or its quantity changes. Use it for addon-specific rules (compatibility, min/max, account policy).

<CodeGroup>
  ```ts TypeScript theme={null}
  import { Addon } from '@stigg/react-sdk';

  onAddonChange={async ({ addon, quantity }) => {
    const validation = // some asynchronous validation
            if (validation) {
              return { errorMessage: `Validation failed for addon: ${addon.displayName}`, disableNextStep: true };
            }
            return { disableNextStep: false };
          }}
  ```
</CodeGroup>

#### `onBeforeStepChange`

Intercepts navigation between checkout steps (e.g., `ADDONS` to `PAYMENT`). Return false to block the step change and show your own messaging.

<CodeGroup>
  ```ts TypeScript theme={null}
  onBeforeStepChange={async ({ from, to, state }) => {
    if (from === 'ADDONS' && to === 'PAYMENT') {
      const validation = // some asyncronous validation
      if (!validation) {
        window.alert('Validation failed for addons'); // show a custom component to the user
        return false;
      }
    }
    return true;
  }}
  ```
</CodeGroup>

## Customization options

The widgets that are included in this package include a default theme, which can be customized to match the appearance of your application.

### Global theming

You can pass customization options such as theming and locale to `StiggProvider` component. Doing so will affect **all Stigg widgets** that are descendent to the provider.

<CodeGroup>
  ```javascript React theme={null}
  import React from 'react';  
  import ReactDOM from 'react-dom';  
  import { StiggProvider } from '@stigg/react-sdk';  
  import App from './App';

  // Example of the options that are available for the theme  
  const theme = {  
    palette: {  
      primary: '#FFA500',  
      backgroundPaper: '#fcfbf8',  
      backgroundHighlight: '#FFF3E0',  
      outlinedHoverBackground: '#FFE0B2',
      text: {
        primary: '#333bf8',
        secondary: '#fcf222',
        disabled: '#fcf111',
      },
    },  
    layout: {  
      planMinWidth: '250px',  
      planMaxWidth: '250px',  
      ctaAlignment: 'center',  
      headerAlignment: 'center',  
      descriptionAlignment: 'center',  
    },  
    typography: {
      fontFamily: 'custom-font, DM Sans, sans-serif',
      h1: {
        fontSize: '32px',
        fontWeight: 'bold',
      },
      h2: {
        fontSize: '24px',
        fontWeight: 'normal',
      },
      h3: {
        fontSize: '16px',
        fontWeight: 'normal',
      },
      body: {
        fontSize: '14px',
        fontWeight: 'normal',
      },
    }
  };

  ReactDOM.render(  
    <StiggProvider apiKey="YOUR_CLIENT_API_KEY" theme={theme} locale="de-DE">  
      <App />  
    </StiggProvider>,  
    document.getElementById('app'),  
  );
  ```
</CodeGroup>

### Widget-specific customization

Each widget can be customized separately via the no-code widget designer in the Stigg app or using code.

Widget-specific customization capabilities can be found under the [dedicated page](#rendering-widgets) of each widget.

## Refreshing the cache

Stigg's SDK refreshes its cache of customer entitlements and usage data upon initialization (for example: when a page is refreshed), as well as periodically every 30 seconds.

After performing an operation on the backend that can modify the customer entitlements or the usage of a feature (for example: updating subscriptions or reporting usage), it's useful to immediately refresh the cache.

To do so, call the below method:

<CodeGroup>
  ```typescript React theme={null}
  import { useStiggContext } from '@stigg/react-sdk';

  function App() {
    const { stigg, refreshData } = useStiggContext();
    
    const addSeats = async () => {
      // Api call which modify customer entitlements (e.g. add seats or report usage)
      await api.addSeats();
      await refreshData();
    }
  }
  ```
</CodeGroup>

## Offline mode

During local development or testing, you might want to avoid making network requests to the Stigg API. To do this, you can run the React SDK in offline mode by enabling the offline option. When enabled, API key validation will always succeed, regardless of the key provided.

<CodeGroup>
  ```typescript React theme={null}
  import { StiggProvider } from '@stigg/react-sdk';

  export function App() {    
    return (
      <StiggProvider apiKey="<STIGG_PUBLISHABLE_KEY>" customerId={user.id} offline>
        <NestedComponents />
      </StiggProvider>
    );
  }
  ```
</CodeGroup>

In offline mode, the React SDK respects the [global fallback strategy](/documentation/high-availability-and-scale/local-caching-and-fallback-strategy#global-fallback-configuration), and entitlement evaluations are limited to the values defined as fallback entitlements.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import { StiggProvider } from '@stigg/react-sdk';

  export function App() {    
    return (
      <StiggProvider 
        apiKey="<STIGG_PUBLISHABLE_KEY>" 
        customerId={user.id} 
        offline
        entitlementsFallback={{
          'feature-number-of-seats': {
            hasAccess: true,
          },
          'feature-templates': {
            hasAccess: true,
            usageLimit: 10
          }
      }}>
        <NestedComponents />
      </StiggProvider>
    );
  }
  ```
</CodeGroup>

## Full SDK reference

<Card title="React SDK reference" icon="code" horizontal href="https://react-sdk-docs.stigg.io/" arrow />

## SDK changelog

<Card title="React SDK changelog" icon="clock-rotate-left" horizontal href="/api-and-sdks/changelog/frontend/react" />

## Migrating to v9.0.0

Bumped `@stigg/js-client-sdk` to 4.0.0. The following breaking changes affect react-sdk consumers:

| Change                                                                                                              | Migration                                                                                    |
| ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `entitlementsUpdated` event payload changed from `Map<string, CachedEntitlement>` to `CustomerEntitlementsResultV2` | Update listeners to iterate `.entitlements` (an `EntitlementV2[]` array) instead of a `Map`. |
| `EntitlementsState` type removed                                                                                    | Replace imports with `CustomerEntitlementsResult`.                                           |
| `useNumericEntitlement` on a metered feature now returns a `FeatureTypeMismatch` fallback                           | Use `useMeteredEntitlement` for metered features.                                            |
| `CreditEntitlementCurrency` named export removed                                                                    | Use `CreditEntitlement['currency']` inline type instead.                                     |

## Migration from older SDK versions to v3.x

* Removed CSS class names:
  * `stigg-overview-subscriptions-list-layout`
  * `stigg-overview-subscriptions-list`
  * `stigg-billing-information-layout`
  * `stigg-billing-information-title`
  * `stigg-update-billing-button`
* Paywall `textOverrides.price.paid` paid text customization callback signature was changed: from:

  <CodeGroup>
    ```typescript TypeScript theme={null}
    {
      price: {
        paid?: (planPrice: Price, plan: Plan, selectedBillingPeriod: BillingPeriod) => PlanPriceText;
      }
    }
    ```
  </CodeGroup>

  to:

  <CodeGroup>
    ```typescript TypeScript theme={null}
    {
      price: {
        paid?: (priceData: {
          planPrices: Price[];
          paywallCalculatedPrice?: PaywallCalculatedPricePoint;
          plan: Plan;
          selectedBillingPeriod: BillingPeriod;
        }) => PlanPriceText;
      }
    }
    ```
  </CodeGroup>
