React useState error with LiteYouTubeEmbed in Sanity Portable Text
LiteYouTubeEmbedcomponent to render the Youtube embed I get
Unhandled Runtime Error
Error: React__namespace.useState is not a functionHere's my portable text component:
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'
import { PortableText, PortableTextComponents } from '@portabletext/react'
import { LaunchIcon,LinkIcon } from '@sanity/icons'
import ImageBox from 'components/shared/ImageBox'
import { TimelineSection } from 'components/shared/TimelineSection'
import getYouTubeId from 'get-youtube-id'
import { resolveHref } from 'lib/sanity.links'
import Link from 'next/link'
import React from 'react'
import LiteYouTubeEmbed from 'react-lite-youtube-embed';
import { Block, Image } from 'sanity'
export function CustomPortableText({
paragraphClasses,
value,
}: {
paragraphClasses?: string
value: Block[]
}) {
const components: PortableTextComponents = {
block: {
normal: ({ children }) => {
return <p className={paragraphClasses}>{children}</p>
},
},
marks: {
link: ({ children, value }) => {
return (
<>
<a
className="underline transition hover:opacity-50"
href={value?.href}
target={value?.blank ? '_blank' : '_self'}
rel="noreferrer noopener"
>
{children}
<LaunchIcon className='inline'/>
</a>
</>
)
},
internalLink: ({children, value}) => {
console.log(value)
return (
<>
<Link
className="underline transition hover:opacity-50"
href={resolveHref(value?.toType, value?.slug)}
>
{children}
<LinkIcon className='inline'/>
</Link>
</>
)
}
},
types: {
image: ({
value,
}: {
value: Image & { alt?: string; caption?: string }
}) => {
return (
<div className="my-6 space-y-2">
<ImageBox
image={value}
alt={value.alt}
classesWrapper="relative aspect-[16/9]"
/>
{value?.caption && (
<div className="font-sans text-sm text-gray-600">
{value.caption}
</div>
)}
</div>
)
},
timeline: ({ value }) => {
const { items } = value || {}
return <TimelineSection timelines={items} />
},
youtube: ({value}) => {
const { url } = value
const id = getYouTubeId(url)
return (
<LiteYouTubeEmbed
aspectHeight = {9}
aspectWidth = {16}
id={id}
title={value.alt}
/>
// <a href={value.url}>{value.alt}</a> // adding a link to youtube works fine
// LiteYouTubeEmbed causes Unhandled Runtime Error: React__namespace.useState is not a function
)
}
},
}
return <PortableText components={components} value={value} />
}The error React__namespace.useState is not a function you're encountering is a React version mismatch issue with the react-lite-youtube-embed package, not specifically a Sanity or Portable Text problem. This typically happens in Next.js when a component that uses React hooks isn't properly marked as a Client Component.
The Solution:
Since Next.js 13+ uses the App Router with Server Components by default, you need to mark your CustomPortableText component as a Client Component. Add this directive at the very top of your file:
'use client'
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'
import { PortableText, PortableTextComponents } from '@portabletext/react'
// ... rest of your importsThe 'use client' directive tells Next.js that this component needs to run on the client side, where React hooks like useState (which LiteYouTubeEmbed uses internally) are available.
Alternative Approach - Split Components:
If you want to keep most of your Portable Text rendering on the server, you can create a separate client component just for the YouTube embed:
// components/YouTubeEmbed.tsx
'use client'
import LiteYouTubeEmbed from 'react-lite-youtube-embed'
import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'
export function YouTubeEmbed({ id, title }: { id: string; title: string }) {
return (
<LiteYouTubeEmbed
aspectHeight={9}
aspectWidth={16}
id={id}
title={title}
/>
)
}Then in your main component:
// No 'use client' needed here
import { YouTubeEmbed } from './YouTubeEmbed'
export function CustomPortableText({ paragraphClasses, value }) {
const components: PortableTextComponents = {
// ... your other components
types: {
youtube: ({ value }) => {
const id = getYouTubeId(value.url)
return <YouTubeEmbed id={id} title={value.alt} />
}
}
}
return <PortableText components={components} value={value} />
}This approach keeps your main Portable Text component as a Server Component while only the YouTube embed runs on the client.
Why This Happens:
The react-lite-youtube-embed library uses React hooks internally, which require the React runtime to be available. In Next.js Server Components, React hooks aren't available because the component is rendered on the server. The error message React__namespace.useState suggests the bundler is having trouble resolving React's exports correctly in a server context.
Your code structure looks correct for rendering Portable Text with custom components in Sanity - this is purely a Next.js Client/Server Component issue!
Show original thread1 reply
Was this answer helpful?
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.