Suggestion (backed by a CommentAnnotation with type: 'suggestion'), and surfaces accept/reject actions on the comment dialog.
The accept/reject UI renders on the Velt comment dialog, so your app needs Velt Comments set up — that’s where reviewers act on a suggestion.
How it works
Suggestion mode turns ordinary edits into reviewable proposals instead of letting them write straight to your app. Understanding the pipeline makes every step below make sense:You enable suggestion mode
The SDK starts watching the page. It installs delegated
focusin / change / focusout listeners, so every element tagged with data-velt-suggestion-target="<targetId>" is tracked — including elements added to the DOM later.A user focuses a target
The SDK snapshots the target’s current value as the
oldValue for that edit session.A user edits and commits
On commit, the SDK reads the new value, compares it to the snapshot, and — only if they differ — creates a pending
Suggestion (stored as a CommentAnnotation with type: 'suggestion'). Focusing and blurring without changing anything never creates a suggestion.A reviewer accepts or rejects
Accept/reject buttons render on the comment dialog. Accepting moves the suggestion to
accepted; rejecting moves it to rejected. Because that UI lives on the comment element, the outcome is emitted there as suggestionAccepted / suggestionRejected.“Commit” depends on the input type. For text-like inputs (text, number, date, textarea, contenteditable) the commit signal is
focusout, so each focus session produces at most one suggestion. For atomic inputs (<select>, checkbox, radio) the commit signal is change, because the selection itself is the intent. This is deliberate — the SDK captures intent, not keystrokes.Accept/reject outcomes are emitted on the comment element, not the SuggestionElement. Accepting fires
suggestionAccepted and rejecting fires suggestionRejected — subscribe with useCommentEventCallback (React) or commentElement.on() (other frameworks). The SuggestionElement emits the rest of the lifecycle: suggestionCreated, suggestionStale, targetEditStart, and targetEditCommit. See step 4.Setup
Follow these four steps to add suggestions to your app. Steps 1–2 are the minimum to capture suggestions; step 3 controls how edits become suggestions, and step 4 applies them once accepted. All Suggestions methods live on theSuggestionElement singleton. Get it once and reuse it (the React hooks shown below wrap this for you, so you rarely need the element directly):
- React / Next.js
- Other Frameworks
1. Define suggestion targets
A target is any element you tag withdata-velt-suggestion-target="<targetId>". The targetId is a stable, app-owned identifier — use the same id you use in your own state, not a random UUID (which changes across re-renders and breaks matching).
- React / Next.js
- Other Frameworks
.value / .checked) → textContent. For a single primitive input that’s enough, so a plain <input> needs no registerTarget call. But when one target represents a complex value (an object spanning several controls — e.g. a table row with qty and price), there is no single .value to read. Register a getter so the SDK can snapshot and diff the whole object:
- React / Next.js
- Other Frameworks
registerTarget() returns void. To remove a registration, call unregisterTarget(targetId) — don’t expect an unsubscribe function back.2. Enable suggestion mode
Enabling suggestion mode is what activates the whole pipeline above. It’s global for the current user and not persisted — a page reload returns to normal editing until you enable it again. Pass an optionalEnableSuggestionModeConfig to hook into the capture flow (see step 3).
- React / Next.js
- Other Frameworks
- React / Next.js
- Other Frameworks
3. Capture edits as suggestions
Once a commit is detected, you decide how it becomes a suggestion. There are three approaches, from least to most control. Pick one per target — they’re mutually exclusive for a given edit.Auto-commit with onTargetEditCommit (simplest)
Provide onTargetEditCommit in the enable config. Return an object and the SDK immediately creates the suggestion using your summary / metadata. This is the default path most apps want. onTargetEditStart is informational in v1 (its return value is reserved for future use).
- React / Next.js
- Other Frameworks
Defer the commit with the targetEditCommit event
If you skip onTargetEditCommit, subscribe to the targetEditCommit event. Its payload carries a pre-bound commitSuggestion builder: call it to finalize (optionally overriding summary / metadata), or don’t call it to drop the edit. This lets you gate suggestion creation behind your own logic.
- React / Next.js
- Other Frameworks
Drive it manually with startSuggestion / commitSuggestion
For non-DOM flows — custom widgets, canvas elements, or an “AI proposes a change” button — bypass auto-detection entirely. Call startSuggestion(targetId) to snapshot the current value, then commitSuggestion(config) to create the proposal.
- React / Next.js
- Other Frameworks
commitSuggestion requires suggestion mode to be enabled and the targetId to be registered (tagged or via registerTarget). It rejects (and creates nothing) when mode is off, the target is unknown, or newValue is deeply equal to the snapshot — guarding against no-op suggestions.4. Apply changes when a suggestion is accepted
This is the step you can’t skip. The accept/reject buttons live on the comment dialog, so the outcome is emitted on the comment element assuggestionAccepted (and suggestionRejected). The SDK records the outcome and persists the suggestion — but it’s your handler that applies newValue to your data. Read the change from commentAnnotation.suggestion.
- React / Next.js
- Other Frameworks
If the target DOM node can’t be resolved when a reviewer accepts, the suggestion goes to
stale instead — listen for that on the SuggestionElement with useSuggestionEventCallback('suggestionStale') (React) or suggestionElement.on('suggestionStale') (other frameworks).Read suggestions for your own UI
Beyond the built-in accept/reject buttons, you’ll often want to render your own indicators — a “1 pending change” badge on a row, a custom review panel, or a count in a toolbar. Query suggestions reactively with an optionalSuggestionGetSuggestionsFilter, or fetch the single pending suggestion for a target.
- React / Next.js
- Other Frameworks
Behavior notes
- Drift detection is best-effort: On accept, if a getter is registered, the live value is compared against
oldValue. A mismatch setsdriftDetected: trueon the suggestion. v1 records the flag; a future release will surface a confirmation prompt. - Stale wins over drift: If the target DOM node can’t be resolved at accept time, the suggestion transitions to
staleimmediately and drift detection is skipped.
Lifecycle and events reference
A suggestion moves forward through these states (seeSuggestionStatus):
| Status | Meaning |
|---|---|
pending | Created and awaiting review. |
accepted | A reviewer accepted it; your suggestionAccepted handler applies newValue. |
rejected | A reviewer rejected it (optional rejectReason). Nothing is applied. |
stale | The target DOM node couldn’t be resolved at accept time, so the change can’t be applied. |
apply_failed | Your accept handler threw while applying. Surfaced as a status only — there’s no separate event in v1. |
useCommentEventCallback (React) or commentElement.on() (other frameworks):
| Event (comment element) | Fires when | Payload |
|---|---|---|
suggestionAccepted | A reviewer accepts a suggestion (status: 'accepted') | SuggestionAcceptEvent (annotationId, commentAnnotation, metadata, actionUser) |
suggestionRejected | A reviewer rejects a suggestion | SuggestionRejectEvent (adds rejectReason) |
useSuggestionEventCallback (React) or suggestionElement.on() (other frameworks). They’re defined in SuggestionEventType:
| Event (SuggestionElement) | Fires when | Payload |
|---|---|---|
suggestionCreated | A pending suggestion is created | SuggestionCreatedEvent |
suggestionStale | A suggestion goes stale at accept time | SuggestionStaleEvent |
targetEditStart | A user focuses a target and editing begins | TargetEditStartEvent |
targetEditCommit | An edit is committed (carries the commitSuggestion builder) | TargetEditCommitEvent |
Data model
Suggestions are stored asCommentAnnotation objects with type === 'suggestion' and a populated suggestion field. The full type hierarchy lives in the Suggestions section of the Data Models reference.
