Discussion about fixing a type error in PortableText with React 17/18
This TypeScript error occurs when defining custom list item components in @portabletext/react due to type incompatibility between what TypeScript expects for a JSX component and what you're providing.
The issue typically happens when you define a listItem component in your components object. The error message indicates that TypeScript sees the Li component type as ReactElement | Component, which isn't a valid JSX element type signature.
The Solution:
Make sure your listItem component is defined as a proper functional component that returns JSX, not as a class component or mixed type. Here's the correct way to type it:
import { PortableText, PortableTextComponents } from '@portabletext/react'
const components: PortableTextComponents = {
list: {
bullet: ({children}) => <ul className="custom-bullet-list">{children}</ul>,
number: ({children}) => <ol className="custom-number-list">{children}</ol>,
},
listItem: {
bullet: ({children}) => <li className="custom-bullet-item">{children}</li>,
number: ({children}) => <li className="custom-number-item">{children}</li>,
},
}
// Then use it:
<PortableText value={portableTextContent} components={components} />Key Points:
- Use arrow functions: Define your components as arrow functions that accept props and return JSX directly
- Separate list and listItem: The
listproperty controls the wrapper (<ul>or<ol>), whilelistItemcontrols individual items (<li>) - Match list types: Both
listandlistItemcan havebulletandnumberproperties to handle different list styles
If you're using custom types:
import type { PortableTextComponents, PortableTextListItemBlock } from '@portabletext/react'
const components: PortableTextComponents = {
listItem: ({children, value}) => {
// value gives you access to the full block data if needed
return <li>{children}</li>
}
}The error usually resolves when you ensure your component definitions are consistently typed as functional components. Avoid mixing class components, or returning conditional types that might be ReactElement in some cases and Component in others. Keep them as simple functional components that always return JSX.
For more details on customizing Portable Text rendering, check out the guide on customizing Portable Text from schema to React component.
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.