Docs navigation
<CancelFlow>
The drop-in cancel flow component.
import { CancelFlow } from '@churnkey/react'
import '@churnkey/react/styles.css'Mode selection
| Prop | Type | Required | Default |
|---|---|---|---|
appId | string | analytics + connected | — |
customer | DirectCustomer | analytics | — |
subscriptions | DirectSubscription[] | optional | — |
session | string | connected | — |
mode | 'live' | 'test' | optional | 'live' |
apiBaseUrl | string | optional | Churnkey production API |
apiBaseUrl is for self-hosted Churnkey deployments only. Leave it unset in normal use.
Flow definition
| Prop | Type | Notes |
|---|---|---|
steps | Step[] | Required in open source and analytics. Optional in connected mode; the dashboard supplies them. |
Handlers
Action callbacks. In open source and analytics they're the only way an action happens — the SDK doesn't call your billing API on its own. In connected mode, defining one overrides Churnkey's server action for that offer type only.
| Prop | Signature |
|---|---|
handleDiscount | (offer: AcceptedOffer, customer) => Promise<void> | void |
handlePause | (offer: AcceptedOffer, customer) => Promise<void> | void |
handlePlanChange | (offer: AcceptedOffer, customer) => Promise<void> | void |
handleTrialExtension | (offer: AcceptedOffer, customer) => Promise<void> | void |
handleCancel | (customer) => Promise<void> | void |
Handlers receive an AcceptedOffer: the offer config plus reasonId and (for custom offers) result. AcceptedOffer is a union over every offer type, so narrow on offer.type to read type-specific fields like couponId or months. See Callbacks for the narrowing pattern.
Listeners
Side-effect callbacks. They fire after the action commits — analytics, toasts, route changes. The per-type listener runs first; the catch-all onAccept runs after, for every offer type. Listener errors are swallowed so they can't break the flow.
| Prop | Signature |
|---|---|
onAccept | (offer, customer) => void | Promise<void> |
onDiscount | (offer, customer) => void | Promise<void> |
onPause | (offer, customer) => void | Promise<void> |
onPlanChange | (offer, customer) => void | Promise<void> |
onTrialExtension | (offer, customer) => void | Promise<void> |
onCancel | (customer) => void | Promise<void> |
onClose | () => void |
onStepChange | (step, prevStep) => void |
Customization
| Prop | Type | Notes |
|---|---|---|
appearance | Appearance | Color scheme, CSS variable overrides. |
classNames | StructuralClassNames | className overrides for modal, overlay, close, back. |
components | ComponentOverrides | Replace built-in UI components. |
customComponents | Record<string, Component> | Components for custom step and offer types, keyed by type. |
Appearance
interface Appearance {
colorScheme?: 'light' | 'dark' | 'auto'
variables?: Partial<AppearanceVariables>
}
interface AppearanceVariables {
// Surfaces
colorBackground: string
colorSurface: string
colorSurfaceMuted: string
// Borders
colorBorder: string
colorBorderStrong: string
// Text
colorText: string
colorTextSecondary: string
colorTextMuted: string
// Primary
colorPrimary: string
colorPrimaryHover: string
colorPrimarySoft: string
// Semantic
colorSuccess: string
colorSuccessSoft: string
colorDanger: string
colorDangerHover: string
colorDangerSoft: string
// Typography
fontFamily: string
fontFamilyMono: string
fontFamilyDisplay: string
fontSize: string
fontWeightDisplay: string
letterSpacingDisplay: string
// Geometry
borderRadius: string
radiusSm: string
radiusMd: string
radiusLg: string
radiusXl: string
// Elevation
shadowModal: string
shadowCard: string
// Overlay
overlayColor: string
}See Theming for usage.
StructuralClassNames
interface StructuralClassNames {
modal?: string
overlay?: string
closeButton?: string
backButton?: string
}Per-step classNames go on the step config, not here. See classNames.
Built-in close behavior
The default modal closes when the customer clicks the close button, clicks the overlay, or presses Escape. Each fires onClose. Override the Modal component slot to change this behavior.
Subpath exports
The package has three entry points:
import { CancelFlow } from '@churnkey/react' // drop-in component
import { useCancelFlow } from '@churnkey/react/headless' // headless hook
import { CancelFlowMachine } from '@churnkey/react/core' // state machine, no React dependencyThe core export is the state machine without React — useful for testing flow logic in isolation or wiring it into a non-React framework.
Next steps
useCancelFlow— the headless hook with the same config.- Step types — every built-in step and the custom-step shape.
- Offer types — every built-in offer and
AcceptedOffer.