DEV Community

Cover image for Rendering Rich Text Safely in TanStack Start with Directus, DOMPurify & Tailwind Typography
Wade Thomas
Wade Thomas

Posted on

Rendering Rich Text Safely in TanStack Start with Directus, DOMPurify & Tailwind Typography

If you're using Directus as a headless CMS, the built-in WYSIWYG editor outputs raw HTML. The challenge is rendering it in React safely, with proper styling, and without reaching for dangerouslySetInnerHTML.

Here's how to solve it with three packages working together.

The Stack

  • DOMPurify — sanitizes HTML, stripping XSS vectors before they touch the DOM
  • html-react-parser — converts sanitized HTML into React elements
  • Tailwind CSS v4 Typography — styles everything automatically with the prose class

Installing the Packages

npm install dompurify html-react-parser
npm install @tailwindcss/typography
Enter fullscreen mode Exit fullscreen mode

Step 1 — Sanitize the HTML

Create a reusable utility function that runs DOMPurify on any HTML string coming from Directus. The typeof window === 'undefined' guard handles SSR — DOMPurify is browser-only.

export const sanitizeHtml = (html: string) => {
  if (typeof window === 'undefined') return html;
  return DOMPurify.sanitize(html);
};
Enter fullscreen mode Exit fullscreen mode

Step 2 — Parse and Render

Pass the sanitized HTML through html-react-parser to convert it into React elements. Wrap it in a prose div and Tailwind Typography handles all the styling.

import parse from 'html-react-parser';
import { sanitizeHtml } from '@/utils/sanitizeHtml';

<div className="prose dark:prose-invert prose-ul:text-foreground
  prose-li:marker:text-foreground prose-strong:text-foreground max-w-none">
  {parse(sanitizeHtml(product.description))}
</div>
Enter fullscreen mode Exit fullscreen mode

What You Get

  • ✅ XSS safe — DOMPurify strips malicious scripts before rendering
  • ✅ No dangerouslySetInnerHTML — html-react-parser converts to React elements directly
  • ✅ Dark mode ready — prose-invert handles the color flip automatically
  • ✅ Design system aware — prose-strong:text-foreground and prose-ul:text-foreground pull from your CSS variables so text always matches your theme
  • ✅ Headings, lists, bold, spacing — all styled automatically by Tailwind Typography

Why Not Just Use dangerouslySetInnerHTML?

dangerouslySetInnerHTML renders raw HTML with no sanitization. If your CMS content ever contains injected scripts — whether from a compromised editor, a bad import, or a malicious user — it executes directly in the browser. DOMPurify + html-react-parser gives you the same rendered output with none of the risk.


This pattern works with any headless CMS that outputs HTML from a rich text editor — Directus, Payload, Strapi, or Contentful. One utility function and a prose wrapper is all it takes.

Top comments (0)