Discussion about troubleshooting image display in Sanity.io using Portable Text and Tailwind CSS.

27 replies
Last updated: Sep 14, 2022
So, my rich text is showing up on my posts but my images are not - I'm using the plain old blockContent image...? Any help as to what I'm missing here?
AI Update

I can see from your description that you're running into a common issue with rendering images in Portable Text (block content). The text is showing up fine, but images aren't rendering. This typically happens because image references in Portable Text aren't automatically resolved - you need to handle them explicitly.

The Problem

When you add images to a Portable Text field in Sanity, they're stored as references to asset objects rather than complete image data. These references need to be manually resolved in both your GROQ query and your rendering component.

The Solution

You need to do two things:

1. Update your GROQ query to resolve image references

You need to explicitly resolve the asset reference for images within your Portable Text field:

*[_type=="yourDocumentType"]{
  title,
  body[] {
    ...,
    _type == "image" => {
      "image": asset->url
    }
  }
}

The key part is body[] { ..., _type == "image" => { "image": asset->url } } which loops through your Portable Text blocks and resolves any image assets. The ... means "include everything else as-is", and the _type == "image" => part is a conditional that only applies to image blocks.

2. Migrate to @portabletext/react

If you're using @sanity/block-content-to-react, that library is now deprecated. You should migrate to the modern @portabletext/react library:

npm install @portabletext/react

Then update your component:

import { PortableText } from '@portabletext/react'

const components = {
  types: {
    image: ({value}) => <img src={value.image} alt="" />
  }
}

// In your component
<PortableText value={yourContent} components={components} />

Important: With @portabletext/react, the prop name changed from node to value, so you access the image URL as value.image instead of node.image.

Optional Enhancements

You can query additional image metadata for better styling control:

_type == "image" => {
  "image": asset->url,
  "dimensions": {
    "width": asset->metadata.dimensions.width,
    "height": asset->metadata.dimensions.height,
    "aspectRatio": asset->metadata.dimensions.aspectRatio
  }
}

You can also add custom fields to your image schema (like a "full width" toggle) that you can query and use to control rendering in your component.

The Sanity documentation on presenting block text has a complete migration guide covering the API differences between the old and new libraries.

Can you log
value
to see what it is?
Yes, console.log the value and then
*also*: Are you using next or basic react?

*And*: Have you fully defined your builder? I do it like this:

import createImageUrlBuilder from '@sanity/image-url'
import { sanityConfig } from './config'

export const imageBuilder = createImageUrlBuilder(sanityConfig)

export const urlForImage = (source) =>
  imageBuilder.image(source).auto('format').fit('max') // but I mostly use next-sanity-image 
Here is my image builder?
const builder = imageUrlBuilder(client);

export function urlFor(source: SanityImageSource) {
  return builder.image(source);
}
I'm using Remix
could you also log value and see if any other errors or warnings exist?
user J
user J
could you show us your imports as well, I am now unsure, why you use the
urlBuilder
when you have
urlFor()
setup with your client, since you don’t use your
client
in
PostImage
.
You are getting the right value
Sure thing:
I'm only using urlBuilder because that's what the example has? Should I be using urlFor() here and if so, how? haha
Replacing urlBuilder with urlFor works!
BUT
yeah, so the image builder needs access to your client, since it gets the referenced data from within your dataset doc.Your screenshots are nice, but also really limiting, because we cannot see your code, btw. so a bit more would go a long way
🙂

What I would do btw: define
urlFor()
where you defined your
client
(like i did here in my
lib/sanity.js
) and which you did here as well .Then import it where you define your portable text component and us this with
urlFor(value)
(or try also
urlFor(value.asset)
if the first does not work… )
import { PortableText } from "@portabletext/react";
import { getImageDimensions } from "@sanity/asset-utils";
import { urlFor } from "~/lib/sanity/sanity";

const SampleImageComponent = ({ value }) => {
  const { width, height } = getImageDimensions(value);
  return (
    <img
      className="flex w-3xl"
      src={urlFor().image(value).auto("format").url()}
      alt={value.alt || " "}
      loading="lazy"
    />
  );
};

export const myPortableTextComponents = {
  types: {
    image: SampleImageComponent,
  },
Here is my entire code?
I've been sending screenshots so that the errors can be seen
yeah but without the code as a whole, this is only a small info 🙂 its not criticism but an implicit ask for more and bigger screenshots. Your last messages were exactly what I was looking for!
user J
Well, I cannot thank you enough for your help - That was the hardest thing I've encountered while working with Sanity. Now to figure out how to style each and every output from the rich text editor!
No problem 🙂
This might help you, while i reproduce your issue and get back at you ….
Oh wait did it work? Your last message sounds like it 🙂
Do you use Tailwind btw? There is some extra functionality around that.
It works!! Thank you 🙂 I saw Geoff mention Proseable text, and tried to incorporate it initially....
But now that I have portable text working, do I just add this Proseable text component to my myPortableTextCompnents file?
Or is proseabletext just extra? Because Tailwind is working for me with Portable Text
It would be a different approach I guess. I tend to work within s simple dyi setup like you made work now, but I think it would depend on the scope of your websites and what you like. I will look into proseable text next week to check it there is any other pros or cons but I always tend to try both early on and see what fits best.
😅 I once rewrote a whole project 3 times (Google MUI, another themplate, SASS +framer and then pivoted to tailwind) but now I know what I like...Plus I'be learnt a lot.
Better to try in the beginning then redo the whole thing is what I mean to say
Alright had a look into prosable text: so basically it is only taking the portable text + tailwind combo another direction: if you want to define, how padding, margins, and everything that is kind of default in PT by your Tailwind setup, you can go the extra way. I never saw the need for myself, but I bet some people really have the need to customise everything the PT gives.
Nice, thank you - I'm finding normal portable text easily editable with Tailwind, so I don't think I'll risk trying to add proseable text into the mix! Thanks again for all your help.
I think so too, love PT once I figured it out it was a game changer 🙂 this is how I found sanity some years ago 🙂

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?