Skip to main content
@pricing/widget is the embeddable end-customer quote component suite — the same components that power the hosted quote page, packaged for your own React app. Components are transport-driven and never self-evaluating: they manage input and view state and call a transport you inject; every amount comes from a server-issued Quote Receipt.

Install

React 18 or newer is required. Import the optional baseline theme once:

Wire it up

Three inputs: a surface config (offerings and factors — ids and labels only), a transport (your server boundary), and optional brand props.
The transport above posts to the anonymous POST /v1/public_quotes route, so the embed needs no API key in the browser. If your compliance posture requires it, proxy the call through your own server instead — the transport contract stays the same.

The same-state-contract promise

Hosted, embedded, and headless surfaces share one public state contract. The framework-neutral createQuoteController drives every surface through the same view states:
  • requires_input preserves the customer’s answers and exposes the dotted missingInputs paths, so the UI can link straight to the unanswered factor.
  • Amounts only ever arrive in Receipts — the widget has no pricing engine and cannot drift from your published Book.
  • A quote created by the embed and a quote created by POST /v1/quotes with the same inputs produce the same Receipt.

Theming

The suite renders structural markup with data-kl-* hooks and kl-* classes, themed by CSS custom properties:
  • Pass brand.accentColor to QuoteProvider — it is applied as --kl-accent on the root element.
  • Override any --kl-* variable (spacing, typography, surfaces) in your own stylesheet to match your design system.
  • Connection branding (productName, logoUrl, accentColor, supportUrl, returnUrl) maps directly onto the brand prop, so an embed matches the hosted page automatically.

Idempotency, handled for you

The controller mints a fresh qi_<uuid> idempotency key for every edited submission and reuses the key only for an unchanged retry. A double-clicked submit replays server-side instead of duplicating; an edited answer never collides with a 409. See Idempotency for the full semantics.

Headless instead?

If you want full control over markup and state, skip the widget and call POST /v1/public_quotes (anonymous) or POST /v1/quotes (API key) directly — the Intent and Receipt shapes are identical. Start with the Quickstart.