RichTextEditor

Tiptap-powered rich text editor for formatted content.

Quick Preview

Rich text editor preview loads in the browser.

Usage

<RichTextEditor defaultValue="<p>Describe the property.</p>" onChange={setHtml} />

Examples

Default RichTextEditor

Use this as the starting point for RichTextEditor. The toolbar covers block styles, marks, colour, lists, alignment, links, quotes, code blocks, images (via URL popover), and undo/redo. Keep product data, routing, fetching, and authorization logic in the consuming app.

Rich text editor preview loads in the browser.

In a form field

Pass an id and point a <FieldLabel htmlFor={id}> at it. The editable surface is a contenteditable region rather than a native input, so a plain label can't focus it natively — the editor wires the association itself, so clicking the label still moves focus into the content.

Rich text editor preview loads in the browser.

Error state

Set aria-invalid to surface a validation error, matching the other form controls. It applies the danger border/ring and reflects aria-invalid on the textbox for assistive tech. Pair it with FieldError for the message.

Rich text editor preview loads in the browser.

With React Hook Form

onChange emits an HTML string (not a DOM event), so wire it up with a Controller rather than register. Forward the field ref to get RHF's focus-on-error behaviour, and spread fieldState into aria-invalid.

import { Controller, useForm } from "react-hook-form";
import { Field, FieldError, FieldLabel } from "@giddaa-housing/ui/field";
import { RichTextEditor } from "@giddaa-housing/ui/rich-text-editor";

function DescriptionField() {
	const { control } = useForm({ defaultValues: { description: "" } });

	return (
		<Controller
			name="description"
			control={control}
			rules={{ required: "A description is required." }}
			render={({ field, fieldState }) => (
				<Field>
					<FieldLabel htmlFor="description">Description</FieldLabel>
					<RichTextEditor
						id="description"
						ref={field.ref}
						value={field.value}
						onChange={field.onChange}
						aria-invalid={fieldState.invalid}
					/>
					<FieldError errors={[fieldState.error]} />
				</Field>
			)}
		/>
	);
}

Props

Prop/APITypeDefaultDescription
value / defaultValuestring-Controlled or uncontrolled HTML content.
onChange(html) => void-Called with next HTML value on every edit.
size`"sm""md""lg"`
placeholderstringwriting hintEmpty-state placeholder text.
disabledbooleanfalseMakes the content read-only and disables the toolbar.
idstring-Associates a <label htmlFor={id}>; clicking it focuses the editor.
aria-invalidbooleanfalseMarks the field invalid; applies the danger border/ring styling.
aria-labelstring"Rich text editor"Accessible name for the editable region.
refRichTextEditorHandle-Exposes focus() / blur() for imperative and form-library focus control.
classNamestring-Local layout or spacing overrides.
native/root propsReact component props-Passed through to the underlying root or primitive.

Accessibility

  • The editable area is a labelled multiline textbox; the toolbar is a labelled toolbar with pressed-state buttons.
  • Provide visible labels or accessible names for interactive controls.
  • Preserve the component-provided focus, keyboard, disabled, and invalid-state behavior.
  • Do not communicate state with color alone; include text, icons, or helper copy.

On this page