Docs navigation
useCancelFlow
The headless hook. Returns the cancel flow's state and actions as React state, leaving rendering to you.
import { useCancelFlow } from '@churnkey/react/headless'
const flow = useCancelFlow(config: FlowConfig)Configuration
useCancelFlow accepts the same FlowConfig as <CancelFlow> minus the rendering props (appearance, classNames, components). Everything else — steps, customer, subscriptions, session, appId, mode, apiBaseUrl, all handle<Type> and on<Type> callbacks, and customComponents — passes through unchanged.
State
| Field | Type | What it is |
|---|---|---|
step | 'survey' | 'offer' | 'feedback' | 'confirm' | 'success' | string | The current step type. Custom types come through as their string value. |
currentStep | ResolvedStep | undefined | The resolved step config (type, title, description, offer, losses, data, etc.). undefined only in connected mode while the config is still loading. |
stepIndex | number | Zero-based position of the current step in the resolved flow. |
totalSteps | number | Total resolved steps. Useful for progress UIs (stepIndex / totalSteps). |
reasons | ReasonConfig[] | The current survey's reasons. Empty when not on a survey step. |
selectedReason | string | null | The reason ID the customer picked. |
currentOffer | OfferDecision | null | The offer on screen. null outside offer steps. |
feedback | string | The feedback textarea value. |
followupResponse | string | Text entered into the follow-up textarea when a freeform: true reason is selected. Empty otherwise. |
outcome | 'saved' | 'cancelled' | null | The terminal state. null until the flow completes. |
isProcessing | boolean | true while a handler or server action is in flight. |
error | Error | null | The last action's error, if any. Cleared on retry. |
customer | DirectCustomer | null | The customer in the flow's state — the customer prop in open source / analytics, or the customer resolved from the token in connected mode. null only when nothing was supplied. |
subscriptions | DirectSubscription[] | The subscriptions passed to the hook, or empty. |
isLoading | boolean | Connected mode only. true while fetching the dashboard-configured flow. |
loadError | Error | null | Connected mode only. The fetch error, if any. |
Actions
| Function | Signature | What it does |
|---|---|---|
selectReason | (id: string) => void | Pick a survey reason. Updates selectedReason. |
next | (result?: object) => void | Advance to the next step. result is captured for custom steps. |
back | () => void | Go back. No-op on the first step. |
accept | (result?: object) => Promise<void> | Accept the current offer. Triggers handle<Type> or the server action. |
decline | () => void | Decline the current offer; advance past it. |
cancel | () => Promise<void> | Confirm cancellation. Triggers handleCancel or the server action. |
close | () => void | Close the flow. Records as abandoned if mid-progress. |
setFeedback | (text: string) => void | Update the feedback value. |
setFollowupResponse | (text: string) => void | Update the follow-up text for the active freeform: true reason. |
retry | () => void | Connected mode only. Re-fetch the dashboard-configured flow after loadError. |
Return shape (typed)
interface UseCancelFlowReturn {
// State
step: 'survey' | 'offer' | 'feedback' | 'confirm' | 'success' | string
currentStep: ResolvedStep | undefined
stepIndex: number
totalSteps: number
reasons: ReasonConfig[]
selectedReason: string | null
currentOffer: OfferDecision | null
feedback: string
followupResponse: string
outcome: 'saved' | 'cancelled' | null
isProcessing: boolean
error: Error | null
customer: DirectCustomer | null
subscriptions: DirectSubscription[]
// Actions
selectReason: (id: string) => void
next: (result?: Record<string, unknown>) => void
back: () => void
accept: (result?: Record<string, unknown>) => Promise<void>
decline: () => void
cancel: () => Promise<void>
close: () => void
setFeedback: (text: string) => void
setFollowupResponse: (text: string) => void
// Connected mode extras
isLoading: boolean
loadError: Error | null
retry: () => void
}Minimal usage
import { useCancelFlow } from '@churnkey/react/headless'
function CancelPage() {
const flow = useCancelFlow({
steps: [/* ... */],
handleCancel: async () => myBilling.cancel(),
})
if (flow.isLoading) return <Spinner />
if (flow.loadError) return <ErrorBanner onRetry={flow.retry} />
switch (flow.step) {
case 'survey': return <MySurvey flow={flow} />
case 'offer': return <MyOffer flow={flow} />
case 'feedback': return <MyFeedback flow={flow} />
case 'confirm': return <MyConfirm flow={flow} />
case 'success': return <MySuccess flow={flow} />
default: return <MyCustomStep flow={flow} type={flow.step} />
}
}For a complete walkthrough, see Headless.
Next steps
<CancelFlow>reference — the same config used by the drop-in component.- Headless — end-to-end headless example.
- Step types — what
flow.stepcan be.