Skip to content
Tier

Correction offer

Pro

A suggested correction for what the person typed: "Did you mean ada@gmail.com?", from a check of the app's (a typo in an email's domain, an address the post office knows better) or a model's. Nothing changes until the person presses Use: no value is applied for them, and nothing is chosen in advance. Keep it mounted under its field and give it a suggestion when there is one (null when there isn't), so an offer that comes while the person works arrives and is announced once, politely.

The suggestion and the original may be a model's or the person's text: each is drawn as plain text in an isolate of its own, so bidi controls in it can't reorder the sentence. Pass fieldId so focus goes back to the field when the offer is taken or dismissed and leaves the page.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2, 4.1.0, 4.2.0 and 4.3.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/correction-offer

Brings in: bidi, button, locale, motion, tokens, use-announcer, utils

Usage

TSX · The example above
"use client"

import * as React from "react"
import { Field } from "@/components/candor/ui/field"
import { Input } from "@/components/candor/ui/input"
import { CorrectionOffer } from "@/components/candor/ui/correction-offer"

const typos: Record<string, string> = { "gmial.com": "gmail.com", "gamil.com": "gmail.com", "outlok.com": "outlook.com" }

// The app's check runs when the person leaves the field; the offer stays mounted and gets a suggestion when there is
// one. Nothing changes until the person presses Use.
export default function Example() {
  const [email, setEmail] = React.useState("ada@gmial.com")
  const [checked, setChecked] = React.useState<string | null>(null)
  const [dismissed, setDismissed] = React.useState<string | null>(null)
  const domain = checked?.split("@")[1] ?? ""
  const fixed = Object.hasOwn(typos, domain) ? `${checked?.split("@")[0]}@${typos[domain]}` : null
  const suggestion = fixed && checked === email && dismissed !== email ? fixed : null
  return (
    <div className="flex w-full max-w-sm flex-col gap-3">
      <Field label="Email" id="offer-email">
        <Input type="email" value={email} onChange={(e) => setEmail(e.target.value)} onBlur={() => setChecked(email)} />
      </Field>
      <CorrectionOffer
        original={email}
        suggestion={suggestion}
        fieldId="offer-email"
        onAccept={() => suggestion && setEmail(suggestion)}
        onDismiss={() => setDismissed(email)}
      />
    </div>
  )
}

<CorrectionOffer>

PropTypeDefaultDescription
originalrequiredstring—What the person typed.
suggestionrequiredstring | null—The suggested value, or `null` while there is none (nothing is drawn). May be a model's text.
reasonstring | undefined—Why it is suggested, in a short line: "Most addresses at this company use gmail.com". May be a model's text, as the suggestion may.
onAcceptrequired() => void—The person chose the suggestion: apply it to the field. Nothing is applied before this.
onDismiss(() => void) | undefined—The person kept what they typed. Left out, the offer has no Keep button and stays until the app takes it away.
fieldIdstring | undefined—The field the offer is about: focus goes back to it when the offer is taken or dismissed.
classNamestring | undefined—Classes for the offer's box.

Strings

The fixed words, in English by default. Change any of them under correctionOffer in <LocaleProvider strings={{ … }}>; the table is exported as correctionOfferStrings.

KeyEnglish
offerDid you mean {suggestion}?
announceDid you mean {suggestion}? Accept or dismiss it after the field.
acceptUse {suggestion}
dismissKeep {original}

Motion

MomentFamilyWhat it does
offerarrive, riseThe offer arrives when a suggestion appears or changes after it was on screen; one there with the page is still.

Set with --cd-motion-correction-offer-<moment>-* in your brand file; the families are in the design reference.