Skip to main content
Follow along with the runnable example. This guide walks through the Rich text insertion example in corti-examples — bootstrap your build from it or use it as reference.
The /transcribe API emits plain transcript segments — it has no knowledge of your editor’s cursor, selection, or surrounding characters. Your application is responsible for splicing each segment into the target field with correct spacing and casing, at the caret. This guide shows the integration pattern: how to take the transcript events the Dictation Web Component emits and insert them into both a plain <textarea> and a formatted (contenteditable) editor.
For the spacing- and casing-boundary rules themselves — when to add a space, when to capitalize, locale-specific handling — see Transcript Text Handling. This page focuses on wiring those rules into a real editor.

Listen for transcript events

The Web Component emits a transcript event for each segment. Preview interim segments (isFinal: false) outside the document, and commit only final segments (isFinal: true) into the editor. See the rich text insertion example in corti-examples for the full event wiring.
Keep a single active interim span and replace it as newer interim results arrive. Do not accumulate interim results into committed text — see Handling interim vs final segments.

Write insertion logic once with an adapter

Insertion shouldn’t care whether the target is a <textarea>, a contenteditable surface, or a native control reached through a host bridge. Define a small imperative seam — an editor adapter — and write the spacing/casing logic once against it.
editor-adapter.ts
Each concrete adapter implements the same surface, so the same dictation wiring drives any control.

Splice into a plain-text field

For a <textarea>, read the selection, build the string to insert with the boundary rules, and replace the selected range.
textarea adapter
buildInsertion applies the spacing/casing boundary rules from Transcript Text Handling — pass primaryLanguage so locale-specific spacing (e.g. French non-breaking spaces) is handled, and set capitalize: false when automaticPunctuation is doing casing server-side.

Splice into a rich-text editor

For contenteditable, insert a text node at the caret through the Range/Selection API. Because the node is inserted at the cursor, it inherits any bold/italic formatting active there. The boundary rules are computed against the plain text before the caret, so spacing and casing behave exactly as in the plain-text case.
contenteditable adapter
The contenteditable here is a minimal stand-in — the reusable part is the STT integration (the adapter plus the boundary rules), not the editor. A production app using Tiptap, Lexical, or ProseMirror would implement the same EditorAdapter interface against its own document model; the insertion logic stays identical.

Next steps

Transcript Text Handling

The full spacing, casing, and interim/final rules behind buildInsertion.

Replacements

Apply custom text substitutions to dictated output.

Example code

The complete, runnable Rich text insertion example in corti-examples.
Please contact us for help or questions.