# Rich Text Parameter Utilities

> How to render and customize rich text parameters in Uniform components.

Source: https://docs.uniform.app/docs/sdk/parameter-utilities/rich-text-parameters

Rich text parameters store structured content with formatting (headings, lists, links, bold, italic, etc.) managed through the Uniform rich text editor. The SDK provides the `UniformRichText` component and the `@uniformdev/richtext` package for working with rich text data.

Uniform provides a Vanilla JS rich text-to-HTML renderer as well as components for React and Vue, plus framework-specific packages for Next.js and Nuxt. Each option lets you override how individual rich text nodes are rendered. The examples on this page use React; for Vue and Nuxt, see [`UniformRichText` in the Nuxt SDK](https://docs.uniform.app/docs/sdk/nuxt#uniformrichtext).

## Rendering rich text with UniformRichText

The `UniformRichText` component renders rich text parameters with full formatting support and inline editing capabilities in the Uniform visual editor:

```tsx
import {
  ComponentParameter,
  ComponentProps,
  UniformRichText,
} from "@uniformdev/next-app-router/component";
import { ParameterRichTextValue } from "@uniformdev/richtext";

type ArticleProps = {
  body?: ComponentParameter<ParameterRichTextValue>;
};

export const ArticleComponent = ({
  parameters: { body },
  component,
}: ComponentProps<ArticleProps>) => {
  return (
    <article>
      <UniformRichText
        component={component}
        parameter={body}
        className="prose"
        placeholder="Write your article content here"
      />
    </article>
  );
};
```

## UniformRichText props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `component` | `Pick<ComponentContext, '_id'>` | required | Component context (from `ComponentProps`) |
| `parameter` | `ComponentParameter<ParameterRichTextValue>` | required | The rich text parameter value |
| `as` | `React.ElementType` | `"div"` | Wrapper HTML element. Set to `null` for no wrapper. |
| `className` | `string` | — | CSS class applied to the wrapper |
| `placeholder` | `string \| ((parameter) => string)` | — | Placeholder text shown in the editor when empty |
| `resolveRichTextRenderer` | `RenderRichTextComponentResolver` | — | Custom renderer for rich text nodes |

The `RenderRichTextComponentResolver` type is not exported from the public API, so you cannot import it directly. It has the shape `(node: RichTextNode) => RichTextRendererComponent | null | undefined`, where returning `null` or `undefined` falls back to the default renderer for that node type.

## Checking if rich text is empty

Because the rich text value is a structured JSON object rather than a string, checking whether it's empty is more involved than comparing against an empty string or `undefined`. Use `isRichTextValueConsideredEmpty` from `@uniformdev/richtext` to guard rendering:

```tsx
import { UniformRichText } from "@uniformdev/next-app-router/component";
import { isRichTextValueConsideredEmpty } from "@uniformdev/richtext";

isRichTextValueConsideredEmpty(body?.value) ? null : (
  <UniformRichText component={component} parameter={body} />
);
```

## Custom rich text node renderers

By default, `UniformRichText` maps rich text nodes to standard HTML elements. You can override the rendering of specific node types using `resolveRichTextRenderer`:

```tsx
import { linkParamValueToAnchorProps } from "@uniformdev/canvas-react";
import { RichTextNode, isRichTextNodeType } from "@uniformdev/richtext";

const customResolver = (node: RichTextNode) => {
  // Custom heading rendering
  if (node.type === "heading") {
    return ({ node, children }) => {
      const tag = node.tag || "h2";
      const Tag = tag as keyof JSX.IntrinsicElements;
      return <Tag className="font-display text-brand">{children}</Tag>;
    };
  }

  // Custom link rendering
  if (node.type === "link") {
    return ({ node, children }) => {
      // Narrow to a link node so `node.link` is available under strict TS
      if (!isRichTextNodeType(node, "link")) return null;
      return (
        <a
          {...linkParamValueToAnchorProps(node.link)}
          className="text-blue-600 underline hover:text-blue-800"
        >
          {children}
        </a>
      );
    };
  }

  // Return undefined to use default renderer
  return undefined;
};

// Usage:
<UniformRichText
  component={component}
  parameter={body}
  resolveRichTextRenderer={customResolver}
/>
```

Link nodes carry the link value on `node.link` (`{ type, path, nodeId?, projectMapId?, attributes? }`). The `linkParamValueToAnchorProps` helper from `@uniformdev/canvas-react` converts it to React anchor props: it derives the `href` and includes any [custom link attributes](https://docs.uniform.app/docs/guides/models/components/parameters#link) set by the editor (such as `target`, `rel`, or `data-*`), applying the built-in safeguards (security filtering, automatic `rel="noopener noreferrer"`, and `class` to `className` translation) described in [Link Parameter Utilities](https://docs.uniform.app/docs/sdk/parameter-utilities/link-parameters). The default link renderer applies the same behavior. Outside React (Vue, Nuxt, or HTML string output), use `linkParamValueToHtmlAttributes` from `@uniformdev/richtext`, which returns a plain attribute record.

Note that a custom `className` prop as in the example above overrides any `class` attribute set on the link by the editor; merge the two values if you need both.

## Rendering nested rich text nodes

The `UniformRichText` component looks up its value from the current component, so it does not resolve rich text that is nested inside a block parameter. When you already have a rich text node — such as one nested in a block — render it directly with `UniformRichTextNode`.

A rich text value looks like this, with the top-level node available on `richTextValue.root`:

```js
const richTextValue = {
  root: {
    type: "root",
    version: 1,
    children: [
      {
        type: "paragraph",
        version: 1,
        children: [
          {
            type: "text",
            version: 1,
            text: "text",
          },
        ],
      },
    ],
  },
};
```

Pass the `root` node (or any child node) to `UniformRichTextNode`:

```tsx
import { UniformRichTextNode } from "@uniformdev/canvas-react";

<UniformRichTextNode node={richTextValue.root} />;
```

## Default node type mappings

The SDK provides default renderers for these rich text node types:

| Node Type | Default Rendering |
| --- | --- |
| `heading` | `<h1>` through `<h6>` based on tag level |
| `paragraph` | `<p>` |
| `text` | Inline text with formatting (bold, italic, underline, etc.) |
| `link` | `<a>` |
| `list` | `<ul>` or `<ol>` |
| `listitem` | `<li>` |
| `quote` | `<blockquote>` |
| `code` | `<pre><code>` |
| `linebreak` | `<br>` |
| `table` | `<table><tbody>` |
| `tablerow` | `<tr>` |
| `tablecell` | `<td>` or `<th>` |
| `asset` | `<img>` or media element |
| `tab` | Tab content wrapper |

The `table`, `tablerow`, and `tablecell` nodes require Uniform SDKs `>= 19.181.1`, and the `asset` node requires `>= 19.187.0`.

## Node-specific properties

Some nodes carry additional properties that are useful when writing custom renderers. Each listed helper is exported from `@uniformdev/richtext`.

| Node type | Additional properties | Notes |
| --- | --- | --- |
| `heading` | `tag`: `'h1'`–`'h6'` | Use `tag` to pick the heading level. |
| `paragraph` | `format` (optional): `'center' \| 'end' \| 'justify' \| 'left' \| 'match-parent' \| 'right' \| 'start'`; `direction` (optional): `'ltr' \| 'rtl' \| null` | Use `isPureTextAlign` and `isPureDirection` to decide whether `format` and `direction` should be applied to the `style` and `dir` attributes. |
| `list` | `tag`: `'ul' \| 'ol'`; `start`: `number` | If `start` is `> 1`, set it as the list's `start` attribute. |
| `listitem` | `value`: `number` | If `value` is `> 0`, set it as the list item's `value` attribute. |
| `link` | `link`: `{ type: 'projectMapNode' \| 'url' \| 'tel' \| 'email'; path: string; nodeId?: string; projectMapId?: string; attributes?: Record<string, string>; }` | `path` may include a `#fragment` anchor, and `attributes` holds any [custom link attributes](https://docs.uniform.app/docs/guides/models/components/parameters#link) set by the editor. See [Custom rich text node renderers](#custom-rich-text-node-renderers) for how to keep the default link behavior. |
| `tablecell` | `headerState`: `number` | `0`: no header, `1`: row header, `2`: column header, `3`: row and column header. Use `getRichTextTagFromTableCellHeaderState` to derive the tag. |
| `text` | `format`: `number`; `text`: `string` | Use `getRichTextTagsFromTextFormat` to get the list of tags for a text node from its `format`. |
| `asset` | `__asset` | [Properties](https://docs.uniform.app/docs/guides/composition/manage-assets/asset-parameter#public-api-properties) of the embedded asset. |

## Rendering rich text to plain text

For non-visual use cases (SEO meta descriptions, search indexing, summaries), use `renderToText` from `@uniformdev/richtext`:

```ts
import { renderToText } from "@uniformdev/richtext";

// richTextValue is the raw ParameterRichTextValue
const plainText = renderToText(richTextValue);
```

## Rendering rich text to HTML string

For server-side HTML generation (e.g., RSS feeds, emails), use `renderToHtml`:

```ts
import { renderToHtml } from "@uniformdev/richtext";

const htmlString = renderToHtml(richTextValue);
```

You can override node renderers when generating an HTML string by passing a `resolveRenderer`:

```ts
import { renderToHtml } from "@uniformdev/richtext";

function myParagraphRenderer({ context, renderChildren }) {
  return `<p class="my-paragraph">${renderChildren(context.currentNode.children)}</p>`;
}

const html = renderToHtml(richTextValue.root, {
  resolveRenderer({ currentNode }) {
    if (currentNode.type === "paragraph") {
      return myParagraphRenderer;
    }
  },
});
```

## Import reference

| Export | Package | Description |
| --- | --- | --- |
| `UniformRichText` | `@uniformdev/next-app-router/component` | React component for rendering rich text |
| `UniformRichTextNode` | `@uniformdev/canvas-react` | Render a rich text node directly (e.g. rich text nested in a block parameter) |
| `ParameterRichTextValue` | `@uniformdev/richtext` | Type for rich text parameter values |
| `renderToText` | `@uniformdev/richtext` | Convert rich text to plain text |
| `renderToHtml` | `@uniformdev/richtext` | Convert rich text to HTML string |
| `walkRichTextTree` | `@uniformdev/richtext` | Walk and transform the rich text node tree |
| `isRichTextValue` | `@uniformdev/richtext` | Type guard for rich text values |
| `isRichTextNode` | `@uniformdev/richtext` | Type guard for a rich text node starting from the `root` node |
| `isRichTextNodeType` | `@uniformdev/richtext` | Check whether a node is of a given type |
| `isRichTextValueConsideredEmpty` | `@uniformdev/richtext` | Check if rich text is empty |
| `isPureTextAlign` | `@uniformdev/richtext` | Check whether a paragraph `format` value maps to a text alignment |
| `isPureDirection` | `@uniformdev/richtext` | Check whether a paragraph `direction` value should be applied |
| `getRichTextTagFromTableCellHeaderState` | `@uniformdev/richtext` | Get the tag name (`td`/`th`) for a table cell from its `headerState` |
| `getRichTextTagsFromTextFormat` | `@uniformdev/richtext` | Get the list of tags for a text node from its `format` |
| `linkParamValueToHref` | `@uniformdev/richtext` | Convert a link value to a `href` string (adds the `mailto:`/`tel:` prefix for email and telephone links) |
| `linkParamValueToHtmlAttributes` | `@uniformdev/richtext` | Convert a link value to a sanitized HTML attribute record (`href` plus custom link attributes) |
| `linkParamValueToAnchorProps` | `@uniformdev/canvas-react` | Convert a link value to React anchor props (`class` becomes `className`) |
