👋 Next.js Conf 2024: Come build, party, run, and connect with us! See all events

How to override the default preview configuration for an image block in Portable Text in Sanity.io

6 replies
Last updated: Sep 28, 2022
Hi all. I’m trying to write a
preview
configuration for an
image
block within Portable Text. By default, the image is shown in thumbnail form with its filename as the preview title. In trying to override that I started with the following:
preview: {
  select: {
    media: 'asset',
  },
},
Sanity correctly resolves the reference to the asset and the preview includes a thumbnail of the image, but it appears as “Untitled”. If I also want to retrieve other fields on the asset, e.g. the
altText
, using the dot-notation to follow the reference, it breaks the image handling. This doesn’t work:
preview: {
  select: {
    media: 'asset',
    title: 'asset.altText',
  },
},
It looks like using the dot notation somehow prevents the built-in image handling code from following the reference. I can do this instead, specifically picking out the image url, but it results in the full-size image being loaded which isn’t ideal:

preview: {
  select: {
    image: 'asset',
    alt: 'asset.altText',
    imageUrl: 'asset.url',
  },
  prepare (selection) {
    const { image, alt, imageUrl } = selection;
    return {
      title: alt,
      media: (
        <img src={imageUrl} alt={alt} />
      ),
    };
  },
},
I would need to write more code to request a thumbnailed version of the image using the asset API. Is there a way to avoid this so I can just use a simple
select
without a complicated
prepare
, but still be able to pick out asset fields as well as using the built-in handling of the asset itself? Thanks!
Sep 28, 2022, 11:16 AM
Re that extra code, this is what I’ve ended up with, pulling in the necessary extra fields to build the image URL – it does work, but it would be really good to avoid this boilerplate if the built-in image handling could be made to behave when extra fields on the asset are queried.
import React from 'react';
import SanityImageUrlBuilder from '@sanity/image-url';

const imageUrlBuilder = SanityImageUrlBuilder({
    projectId: process.env.SANITY_STUDIO_API_PROJECT_ID,
    dataset: process.env.SANITY_STUDIO_API_DATASET,
});

export default {
    // ...
    preview: {
        select: {
            asset: 'asset',
            _id: 'asset._id',
            altText: 'asset.altText',
            assetId: 'asset.assetId',
            extension: 'asset.extension',
            metadata: 'asset.metadata',
            originalFilename: 'asset.originalFilename',
            path: 'asset.path',
            url: 'asset.url',
        },
        prepare ({ asset }) {
            const url = imageUrlBuilder.image(asset).width(33).height(33).fit('crop').url();
            return {
                title: asset.altText || asset.originalFilename,
                media: (
                    <img src={url} alt={asset.altText} />
                ),
            };
        },
    },
};
Sep 28, 2022, 11:47 AM
OK, I solved this! When using dot notation the type of the reference gets changed to
sanity.imageAsset
. It’s necessary to set it back to
reference
to allow the default preview component to understand the image:
preview: {
  select: {
    asset: 'asset',
    altText: 'asset.altText',
    originalFilename: 'asset.originalFilename',
  },
  prepare ({ asset, altText, originalFilename }) {
    return {
      title: altText || originalFilename,
      media: {
        ...asset,
        _type: 'reference',
      },
    };
  },
},

Sep 28, 2022, 12:13 PM
I’m not sure how commonplace this use-case is but it might be worth adding a note to this section of documentation to cover it off?
Sep 28, 2022, 12:16 PM
Thanks for walking us through this! I'll flag our docs team to add an example.
Sep 28, 2022, 4:58 PM
Thank you!
Sep 28, 2022, 5:00 PM
Sorry, it was a bit of a journey 😛
Sep 28, 2022, 5:00 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?