Error displaying images in blockcontent, resolved with custom component type

3 replies
Last updated: Jun 23, 2023
Hi newbie here, I’m trying to display images from blockcontent and I got this error:
PortableText [components.type] is missing “image”

And then I follow this page to try to define custom component type:


https://www.sanity.io/guides/ultimate-guide-for-customising-portable-text-from-schema-to-react-component
I’ve added this, but all I got is rendering error
😞 Would appreciate it if someone can point me in the right direction.

const Body = (props)=> {
  // we pass the content, width & height of the images into each instance of the Body component 
  // content is our array of blocks
  const { content, imgWidth, imgHeight } = props;
  
  const customBlockComponents = {
  // first we tackle our custom block types
  types: {
    image: ({value}) => {
      // we need to get the image source url, and since @sanity/image-url will give us optimised images for each instance we use it
      const imgUrl = urlForImage(value.assset).height(imgHeight).width(imgWidth).url()
      
      return <Image
            width={imgWidth}
            height={imgHeight}
            alt={value.alt}
            src={imgUrl}
            sizes="100vw"
            priority={false}
          />
    },
  },

  // then we define how the annotations should be rendered
  marks: {
    link: ({children, value}) => {
      const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined
      return (
        <a href={value.href} target='_blank' rel={rel}>
          {children}
        </a>
      )
    },
    internalLink: ({children, value}) => {
      return (
        <a href={value.href}>
          {children}
        </a>
      )
    },
  },
}

return <PortableText
  value={content}
  components={customBlockComponents}
/>
}
AI Update

I can see the issue! There's a typo in your code that's causing the problem. In your image component, you have:

const imgUrl = urlForImage(value.assset).height(imgHeight).width(imgWidth).url()

Notice value.assset - it should be value.asset (with one 's'). This typo means the image URL builder can't find the image reference, which is causing your rendering error.

Here's the corrected version:

const customBlockComponents = {
  types: {
    image: ({value}) => {
      // Fixed: value.asset instead of value.assset
      const imgUrl = urlForImage(value.asset)
        .height(imgHeight)
        .width(imgWidth)
        .url()
      
      return <Image
        width={imgWidth}
        height={imgHeight}
        alt={value.alt}
        src={imgUrl}
        sizes="100vw"
        priority={false}
      />
    },
  },
  marks: {
    link: ({children, value}) => {
      const rel = !value.href.startsWith('/') ? 'noreferrer noopener' : undefined
      return (
        <a href={value.href} target='_blank' rel={rel}>
          {children}
        </a>
      )
    },
    internalLink: ({children, value}) => {
      return (
        <a href={value.href}>
          {children}
        </a>
      )
    },
  },
}

A few additional tips:

  1. Add error handling: Consider adding a check to ensure the asset exists before trying to build the URL:

    if (!value?.asset) return null;
  2. Alt text fallback: Make sure your image schema includes an alt field, or provide a fallback:

    alt={value.alt || 'Image'}
  3. Check your urlForImage setup: Make sure your urlForImage function is properly configured with your Sanity project details (you can use @sanity/image-url for this).

The approach you're following from that guide is correct - this typo is just preventing it from working! Fix that and you should be good to go. 😊

It looks like
asset
is spelled
assset
in your image handler. That may be causing it to error out.
Ah, I didn’t see that. But I solved it in the end, by changing the codes completely. It worked…:P Thanks for the help anyway!
Glad you got it working 🙂

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.

Was this answer helpful?