@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
Wire it up
Three inputs: a surface config (offerings and factors — ids and labels only), a transport (your server boundary), and optional brand props.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-neutralcreateQuoteController drives every surface through the same view states:
requires_inputpreserves the customer’s answers and exposes the dottedmissingInputspaths, 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/quoteswith the same inputs produce the same Receipt.
Theming
The suite renders structural markup withdata-kl-* hooks and kl-* classes, themed by CSS custom properties:
- Pass
brand.accentColortoQuoteProvider— it is applied as--kl-accenton 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 thebrandprop, so an embed matches the hosted page automatically.
Idempotency, handled for you
The controller mints a freshqi_<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 callPOST /v1/public_quotes (anonymous) or POST /v1/quotes (API key) directly — the Intent and Receipt shapes are identical. Start with the Quickstart.