Skip to main content

Bootstrapping the Configurator

ProductConfigurator is the heart of the SDK: it owns the store, the API client, and the systems (mapping, product rules) that turn a product into a live configuration. This page walks you through the recommended startup sequence, from install to the moment your hooks become safe to use.

Step 1: Install and import

import { ProductConfigurator } from "@expivi/product-configurator";

Step 2: Register the UI web components (optional)

If your UI uses Expivi web components (the xpv-* elements), register them once before you mount your app. Each package ships a defineCustomElements function:

import { defineCustomElements as defineCustomElementsOfUiKit } from "@expivi/ui-kit";
import { defineCustomElements as defineCustomElementsOfProductOptions } from "@expivi/ui-composer";
import { defineCustomElements as defineCustomElementsOfFormRenderer } from "@expivi/form-renderer";
import { defineCustomElements as defineCustomElementsOfOfferInquiry } from "@expivi/offer-inquiry";
import { defineCustomElements as defineCustomElementsOfPriceBreakdown } from "@expivi/price-breakdown";
import { defineCustomElements as defineCustomElementsOfBomBreakdown } from "@expivi/bom-breakdown";

defineCustomElementsOfUiKit();
defineCustomElementsOfProductOptions();
defineCustomElementsOfFormRenderer();
defineCustomElementsOfOfferInquiry();
defineCustomElementsOfPriceBreakdown();
defineCustomElementsOfBomBreakdown();

If you are building a fully custom UI on top of the hooks, you can skip this step and register only the packages you actually render.

Step 3: Create the ProductConfigurator

Construct the configurator with your API credentials and optional locale metadata. apiBaseUrl and apiToken are the only required options; everything else has a sensible default.

const productConfigurator = new ProductConfigurator({
apiBaseUrl: "https://api.example.expivi.net",
apiToken: "your-api-token",
locale: "en-US",
country: "us",
currency: "EUR",
});

Constructor options

OptionTypeRequiredDefaultDescription
apiBaseUrlstringyesBase URL for the Expivi API.
apiTokenstringyesAccess token used by the generated service clients.
localestringno"en-US"Locale for the API client and formatting-sensitive UI.
countrystringno"us"Country passed into the API client global config.
currencystringno"EUR"Currency passed into the API client global config.
versionstringno"1.0.0"Version metadata.
snapshotIdstringnoSnapshot to load automatically once the product is ready.
getConfigurationLink(snapshotId: string) => stringnoBuilds the shareable configuration link used by quote/download flows. Must be a function if provided.

The constructor validates its input and throws if apiBaseUrl or apiToken is missing, or if getConfigurationLink is provided but is not a function. On success it creates the store and sets the status to INIT.

Step 4: Load a product

await productConfigurator.loadProduct(productId);

loadProduct(...) drives the configurator through its lifecycle. Each stage sets a ProductConfiguratorStatus you can observe (see the app shell guide):

  1. Status → LOADING.
  2. Loads the product offer.
  3. Loads the product layout.
  4. Status → LOADED.
  5. Initializes the internal systems (mapping engine, product rules engine).
  6. Status → READY.
  7. If snapshotId was configured, loads that saved configuration.

The full set of statuses is INIT, LOADING, LOADED, READY, ERROR, and DESTROYED. If loading fails, the status is set to ERROR and an error message is added to the store.

Step 5: Create hooks once the store is populated

Most hook controllers (everything built on HookController) require the configurator to already be initialized. They throw Product configurator is not initialized. unless the status is LOADED or READY, so create them only after loadProduct has reached at least the LOADED stage.

The one exception is useProductConfigurator, which is safe to create at any time — it exists specifically so your shell can observe the lifecycle before the product is ready.

Full example

import { ProductConfigurator } from "@expivi/product-configurator";

const productConfigurator = new ProductConfigurator({
apiBaseUrl: "https://api.example.expivi.net",
apiToken: "your-api-token",
});

await productConfigurator.loadProduct(productId);

// Safe now that the configurator is READY:
const productStore = useStoreProductOffers(productConfigurator);
const layout = useProductOfferLayout(productConfigurator);

If you would rather not block on the load, you can kick it off without awaiting and let your app shell react to the status instead:

const productConfigurator = new ProductConfigurator({ apiBaseUrl, apiToken });

void productConfigurator.loadProduct(productId);

// Mount your UI immediately; it renders a loader until status is READY.

See Understanding the App Shell for that non-blocking pattern in detail.