rapidUI

Rich Text Content

Renders HTML produced by the rich text editor with consistent typography.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/rich-text-content.json

Installs to @/components/ui/rich-text-content.

Manual installation

Copy the source into your project:

components/ui/rich-text-content.tsx
import type * as React from 'react'
import { cn } from '@/lib/utils'

interface RichTextContentProps
	extends Omit<React.HTMLAttributes<HTMLDivElement>, 'dangerouslySetInnerHTML' | 'children'> {
	html: string
}

/**
 * Renders rich text HTML content with proper list, paragraph, and link styles.
 * Use this wherever richtext from the Tiptap editor is displayed as output.
 */
export function RichTextContent({ html, className, ...props }: RichTextContentProps) {
	return (
		<div
			data-slot="rich-text-content"
			className={cn(
				'[&_p]:m-0 [&_p]:w-full [&_p:empty]:min-h-[1em] [&_ul]:list-disc [&_ul]:pl-6 [&_ol]:list-decimal [&_ol]:pl-6 [&_li]:pl-1 [&_a]:text-primary [&_a]:underline [&_a]:underline-offset-4',
				className,
			)}
			// biome-ignore lint/security/noDangerouslySetInnerHtml: richtext content from editor
			dangerouslySetInnerHTML={{ __html: html }}
			{...props}
		/>
	)
}

Renders an HTML string produced by a rich text editor (Tiptap, ProseMirror, …) with paragraph, list and link styles applied, so the output looks the same as it did in the editor.

The component uses dangerouslySetInnerHTML — only pass markup you trust or have sanitized server-side.

Usage

import { RichTextContent } from "@/components/ui/rich-text-content"

<RichTextContent html={decision.body} className="text-sm" />

Examples

Default

A news article with a paragraph, a bullet list and a link.

Muted

Case notes rendered in a smaller, muted style with className.

Decision Letter

A decision with a numbered list of conditions, inside a card.