Skip to content

feat(richtext-lexical): add custom component support to LinkJSXConverter and UploadJSXConverter#15577

Open
tobiasvdorp wants to merge 2 commits intopayloadcms:mainfrom
tobiasvdorp:feat/richtext-lexical-custom-link-upload-components
Open

feat(richtext-lexical): add custom component support to LinkJSXConverter and UploadJSXConverter#15577
tobiasvdorp wants to merge 2 commits intopayloadcms:mainfrom
tobiasvdorp:feat/richtext-lexical-custom-link-upload-components

Conversation

@tobiasvdorp
Copy link

@tobiasvdorp tobiasvdorp commented Feb 10, 2026

Summary

Adds support for passing custom LinkComponent and ImageComponent into LinkJSXConverter and UploadJSXConverter, so Next.js apps (and other frameworks) can use their own Link and Image without copying the full converter logic into their project.

Problem

  • LinkJSXConverter hardcodes <a> with no option for a custom link component (e.g. next/link).
  • UploadJSXConverter hardcodes <a> and <img> with no option for next/image or other framework components.
  • Using framework-specific components currently requires copying and maintaining ~70–90 lines of converter logic per converter.

Changes

LinkJSXConverter — Added optional LinkComponent prop:

  • When provided, renders the custom component instead of <a> for both autolink and link nodes
  • Passes href, rel, target, and children
  • When omitted, behavior is identical to before — no breaking change
  • internalDocToHref continues working as before

UploadJSXConverter — Converted from static object to function with optional parameters:

  • ImageComponent — Custom component for <img> elements (images without sizes + fallback inside <picture>)
  • LinkComponent — Custom component for non-image file links
  • buildFullUrl — Transforms upload paths into absolute URLs (e.g. for CDN/cloud storage)
  • UploadJSXConverter() with no arguments produces identical behavior to before
  • Breaking change: Direct consumers who spread UploadJSXConverter as an object (...UploadJSXConverter) must update to ...UploadJSXConverter()

defaultConverters.ts — Updated to call ...UploadJSXConverter().

Documentation — Added a "Custom Components" section to docs/rich-text/converting-jsx.mdx with Next.js examples for LinkComponent, ImageComponent, and buildFullUrl.

Example usage

import Link from 'next/link'
import Image from 'next/image'
import {
  RichText,
  LinkJSXConverter,
  UploadJSXConverter,
} from '@payloadcms/richtext-lexical/react'

const jsxConverters = ({ defaultConverters }) => ({
  ...defaultConverters,
  ...LinkJSXConverter({
    internalDocToHref: ({ linkNode }) => `/${linkNode.fields.doc.value.slug}`,
    LinkComponent: ({ href, children, ...rest }) => (
      <Link href={href} {...rest}>{children}</Link>
    ),
  }),
  ...UploadJSXConverter({
    buildFullUrl: (path) => `https://cdn.example.com${path}`,
    ImageComponent: ({ alt, height, src, width }) => (
      <Image src={src} alt={alt ?? ''} width={width ?? 0} height={height ?? 0} />
    ),
  }),
})

Test plan

  • Verify LinkJSXConverter({}) still works (no LinkComponent) — renders <a>
  • Verify LinkJSXConverter({ LinkComponent: ... }) renders the custom component for both autolink and link nodes
  • Verify UploadJSXConverter() with no args produces same output as before
  • Verify UploadJSXConverter({ ImageComponent, LinkComponent, buildFullUrl }) uses custom components and URL transformation
  • Verify defaultJSXConverters still works unchanged for consumers
  • Verify buildFullUrl is applied to both the main image URL and all size URLs inside <picture>

…ter and UploadJSXConverter

Co-authored-by: Cursor <cursoragent@cursor.com>
@tobiasvdorp tobiasvdorp marked this pull request as draft February 10, 2026 16:15
Co-authored-by: Cursor <cursoragent@cursor.com>
@tobiasvdorp tobiasvdorp marked this pull request as ready for review February 11, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant