Pre-checked paid add-ons where the cost is only visible in the order total — not adjacent to the checkbox — are identified in the FTC's Dark Patterns Report as a manipulative checkout pattern. The Negative Option Rule also applies when a pre-checked add-on carries a recurring charge. Pre-checked marketing opt-ins at checkout additionally violate GDPR consent requirements for EU users and the CAN-SPAM Act's opt-in standards. These patterns collectively drive consumer complaints, chargebacks, and regulator attention across multiple frameworks.
Low because discovering a pre-checked add-on requires active user attention during checkout rather than post-purchase, reducing immediate harm — but the pattern is explicitly called out in the FTC Dark Patterns Report and compounds with Negative Option Rule exposure for recurring charges.
Label all pre-selected add-ons with their cost immediately adjacent to the checkbox, and default marketing opt-ins to unchecked.
function AddOnOption({
label, price, interval, defaultChecked = false, onChange
}: AddOnProps) {
const [selected, setSelected] = useState(defaultChecked)
return (
<label className="flex items-start gap-3 cursor-pointer">
<input
type="checkbox"
checked={selected}
onChange={e => { setSelected(e.target.checked); onChange?.(e.target.checked) }}
className="mt-1"
/>
<div>
<span className="font-medium">{label}</span>
{/* Cost must appear adjacent to the checkbox — not only in the order total */}
<span className="text-sm text-gray-600 ml-2">+${price}/{interval}</span>
{defaultChecked && (
<span className="text-xs text-amber-600 ml-2">
(pre-selected — uncheck to remove)
</span>
)}
</div>
</label>
)
}
For annual billing as default: show 'Billed as $288/year' prominently next to the option — not just the per-month equivalent. Marketing opt-ins must default to unchecked in all checkout flows.
ID: ftc-consumer-protection.dark-patterns.pre-checked-upsells-labeled
Severity: low
What to look for: Count all relevant instances and enumerate each. Examine checkout forms and multi-step purchase flows for pre-selected checkboxes or toggles that add cost. Look for: (1) add-on products or services that are checked by default and add to the purchase total; (2) optional insurance, warranty, or protection plans that are selected by default; (3) plan upgrades that are pre-selected with a note that the user must "downgrade" to a cheaper tier; (4) annual billing selected by default when monthly is also available (acceptable if clearly labeled, problematic if the default's annual cost is not immediately visible). Also look for "opt-out" consent for marketing subscriptions pre-checked in checkout forms — this is both a dark pattern and a GDPR violation for EU users.
Pass criteria: Add-ons that increase the purchase price are either not pre-selected, or are clearly labeled with their cost immediately adjacent to the checkbox/toggle. At least 1 implementation must be verified. Deselecting them is trivially easy (one click). Marketing email opt-ins are not pre-checked. Annual billing as default is acceptable if the annual total is prominently shown alongside the checkbox.
Fail criteria: Optional paid add-ons are pre-checked with no visible cost label adjacent to the checkbox. The cost increase from a pre-selected add-on is only visible in the order total, not next to the checkbox. Marketing opt-in is pre-checked at checkout.
Skip (N/A) when: The application has no checkout flow, no add-ons, and no optional paid features.
Detail on fail: Example: "Checkout form pre-checks 'Priority Support' add-on (+$10/month) with no cost shown next to the checkbox. Cost is only visible in the order total." or "Annual billing plan pre-selected by default; the $288/year charge is not shown until the final confirmation step." or "Marketing newsletter opt-in checkbox is pre-checked at checkout."
Remediation: Label all pre-selected items with their cost and make deselection obvious:
// Pre-selected add-on — with clear cost label
function AddOnOption({
label,
price,
interval,
defaultChecked = false,
onChange,
}: AddOnProps) {
const [selected, setSelected] = useState(defaultChecked)
return (
<label className="flex items-start gap-3 cursor-pointer">
<input
type="checkbox"
checked={selected}
onChange={e => {
setSelected(e.target.checked)
onChange?.(e.target.checked)
}}
className="mt-1"
/>
<div>
<span className="font-medium">{label}</span>
{/* Cost must be immediately adjacent to the checkbox */}
<span className="text-sm text-gray-600 ml-2">
+${price}/{interval}
</span>
{defaultChecked && (
<span className="text-xs text-amber-600 ml-2">
(included by default — uncheck to remove)
</span>
)}
</div>
</label>
)
}
If annual billing is the default, show the annual total prominently: "Billed as $288/year" — not just the monthly equivalent. Marketing opt-ins should always default to unchecked in the checkout flow.