@amitdhoju/tiptap-ui
v1.5.0
Published
A pre-styled, self-contained React rich text editor built on Tiptap — controlled value/onChange API, works with any form library (react-hook-form, Formik, plain React state), no consumer Tailwind/shadcn setup required.
Maintainers
Readme
@amitdhoju/tiptap-ui
A pre-styled, self-contained React rich text editor built on Tiptap. Controlled
value/onChange API — drop it into a plain useState, a react-hook-form Controller, or any other
form library's controlled-field pattern.
No Tailwind config, no shadcn setup, no extra CSS import required in the consuming app — styles are injected automatically when the component is used.
Install
npm install @amitdhoju/tiptap-uireact and react-dom (17, 18, or 19) must already be present in the host project as peer dependencies.
Everything else (Tiptap, Radix UI primitives, icons, sanitizer) is bundled.
Usage
import { RichTextEditor } from '@amitdhoju/tiptap-ui'
import { useState } from 'react'
const Example = () => {
const [value, setValue] = useState('')
return <RichTextEditor value={value} onChange={setValue} placeholder="Write something..." />
}With react-hook-form
import { RichTextEditor } from '@amitdhoju/tiptap-ui'
import { Controller, type Control, type FieldValues, type Path } from 'react-hook-form'
type Props<T extends FieldValues> = {
control: Control<T>
name: Path<T>
}
const RichTextField = <T extends FieldValues>({ control, name }: Props<T>) => (
<Controller
name={name}
control={control}
render={({ field }) => <RichTextEditor value={field.value} onChange={field.onChange} />}
/>
)Image uploads
Pass onImageUpload to enable the toolbar's image button plus drag-and-drop/paste. Resolve with the
uploaded image's URL — the caller is responsible for the actual upload and for surfacing any error UI.
<RichTextEditor
value={value}
onChange={setValue}
onImageUpload={async (file) => {
const url = await uploadToStorage(file)
return url
}}
/>Sanitizing saved HTML
RichTextEditor stores content as HTML. Before rendering untrusted saved HTML elsewhere (a read-only
view), sanitize it with the exported DOMPurify config:
import { descriptionSanitizeConfig, richTextContentClass } from '@amitdhoju/tiptap-ui'
import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(savedHtml, descriptionSanitizeConfig)richTextContentClass is the same Tailwind class string the editor uses internally for headings, lists,
links, images, etc. — reuse it to make a read-only view visually match the editor's output.
Props
| Prop | Type | Default | Description |
| ---------------- | -------------------------------- | ---------------------- | --------------------------------------------- |
| value | string | undefined | Controlled HTML content. |
| onChange | (value: string) => void | undefined | Called with the new HTML on every edit. |
| placeholder | string | 'Write something...' | Placeholder shown when the editor is empty. |
| minHeight | number | 150 | Minimum height (px) of the content area. |
| readOnly | boolean | false | Renders content without a toolbar or editing. |
| disabled | boolean | false | Same as readOnly, semantically for forms. |
| className | string | undefined | Extra classes on the outer wrapper. |
| onImageUpload | (file: File) => Promise<string> | undefined | Enables image insert/drop/paste. |
An imperative handle exposing getContent(): string is also available via ref.
Development
npm install
npm run build # compiles Tailwind CSS then bundles with tsup
npm run typecheck