All recipes
Default
Default confirm
DefaultConfirm is the component the SDK uses for every confirm step unless you pass components.Confirm. It renders a title, an optional description, an optional losses bullet list, a period-end notice derived from subscriptions[0].status.currentPeriod.end, and a danger-colored cancel button paired with a secondary keep button. Fork it when the confirm screen needs to look fundamentally different — see the Confirm — featured image recipe.
default component
Cancel your subscription?
You'll lose access to:
- Saved projects
- Team comments
- Brand kit
Your access continues until June 14, 2026.
The full source
Copy this file into your project. It's the same file the showcase preview above renders from.
components/MyConfirm.tsxtsx
import { formatPeriodEnd } from '../../core/format'
import type { ConfirmStepProps } from '../../core/types'
import { cn } from '../../core/utils'
import { RichText } from '../rich-text'
export function DefaultConfirm({
title,
description,
subscriptions,
losses,
lossesLabel,
confirmLabel,
goBackLabel,
onConfirm,
onGoBack,
isProcessing,
classNames,
}: ConfirmStepProps) {
const hasLosses = Array.isArray(losses) && losses.length > 0
const periodEnd = formatPeriodEnd(subscriptions)
return (
<div className={cn('ck-step ck-step-confirm', classNames?.root)}>
<h2 className={cn('ck-step-title', classNames?.title)}>{title}</h2>
{description && <RichText html={description} className={cn('ck-step-description', classNames?.description)} />}
{hasLosses && (
<div className={cn('ck-loss-block', classNames?.lossList)}>
<div className={cn('ck-loss-label', classNames?.lossLabel)}>{lossesLabel ?? "You'll lose access to:"}</div>
<ul className="ck-loss-list">
{losses.map((item) => (
<li key={item} className={cn('ck-loss-item', classNames?.lossItem)}>
<span aria-hidden className={cn('ck-loss-bullet', classNames?.lossBullet)}>
●
</span>
<span>{item}</span>
</li>
))}
</ul>
</div>
)}
{periodEnd && (
<p className={cn('ck-period-end', classNames?.periodEndNotice)}>Your access continues until {periodEnd}.</p>
)}
<button
type="button"
className={cn('ck-button ck-button-danger', classNames?.confirmButton)}
onClick={onConfirm}
disabled={isProcessing}
>
{isProcessing ? 'Processing...' : confirmLabel}
</button>
<button type="button" className={cn('ck-button-link', classNames?.goBackButton)} onClick={onGoBack}>
{goBackLabel}
</button>
</div>
)
}
Wire it into a flow
Drop the file into your codebase and reference it from the appropriate prop on <CancelFlow>.
CancelButton.tsxtsx
// The SDK uses DefaultConfirm automatically on confirm steps.
// Copy the source into your own component to fork it from this baseline.
import { CancelFlow } from '@churnkey/react'
<CancelFlow
steps={[
{ type: 'survey', reasons: [/* ... */] },
{
type: 'confirm',
title: 'Cancel your subscription?',
// Optional. Renders a styled bullet list between the description
// and the period-end notice. Naming what the customer loses
// concretely is more honest than a generic line in the description.
losses: ['Saved projects', 'Team comments', 'Brand kit'],
// Optional. Defaults to "You'll lose access to:".
// lossesLabel: 'Cancelling means losing:',
confirmLabel: 'Yes, cancel',
goBackLabel: 'Keep my subscription',
},
]}
handleCancel={async () => myBilling.cancel()}
/>