Adding Image Caption and Attribution to API.js

11 replies
Last updated: Nov 9, 2021
Hi, after adding image caption and attribution to the studio/schemas/post.js:
{
    name: 'mainImage',
    title: 'Main image',
    description: 'Image for displaying on the POST PAGE',
    type: 'image',
    options: {
        hotspot: true
    },
    fields: [
        {
            name: 'caption',
            type: 'string',
            title: 'Caption',
            options: {
                isHighlighted: true            }
        },
        {
            name: 'attribution',
            type: 'string',
            title: 'Attribution',
        }
    ]
},
What do i need to do next to display this on the front end?
AI Update

Great question! After adding those custom fields to your image schema, you need to update your frontend queries and components to fetch and display that data. Here's what you need to do:

1. Update Your GROQ Query

First, modify your query to include the caption and attribution fields. When querying the post, expand the mainImage field to include its custom fields:

const query = `*[_type == "post" && slug.current == $slug][0]{
  title,
  body,
  mainImage {
    asset->{
      _id,
      url
    },
    caption,
    attribution,
    hotspot,
    crop
  }
}`

The key here is that caption and attribution are stored at the same level as asset, hotspot, and crop within the image object, not inside the asset itself.

2. Display the Data in Your Component

Once you have the data, you can render it in your frontend component. Here's an example using React:

import Image from 'next/image'
import { urlFor } from '@/lib/sanity'

function PostImage({ mainImage }) {
  if (!mainImage?.asset) return null
  
  return (
    <figure>
      <Image
        src={urlFor(mainImage).url()}
        alt={mainImage.caption || 'Post image'}
        width={800}
        height={600}
      />
      {mainImage.caption && (
        <figcaption>{mainImage.caption}</figcaption>
      )}
      {mainImage.attribution && (
        <cite>{mainImage.attribution}</cite>
      )}
    </figure>
  )
}

3. Build the Image URL

If you're using the Sanity image URL builder, make sure it's set up in your project:

import imageUrlBuilder from '@sanity/image-url'
import { client } from './sanity'

const builder = imageUrlBuilder(client)

export function urlFor(source) {
  return builder.image(source)
}

The important thing to remember is that custom fields like caption and attribution live on the image field itself (alongside asset, crop, and hotspot), not inside the asset document. So when you query, you access them as mainImage.caption and mainImage.attribution, not mainImage.asset.caption.

This pattern is documented in the Image Type documentation and is particularly useful for storing accessibility information and attribution that's specific to how an image is used in a particular document, rather than metadata that belongs to the asset itself.

Hey
user X
what do you mean by front page? Do you mean in the studio or on your actual front end website?
Hi
user S
, I meant the front end.
You'll need to query for that field, then add it to your component. It's very similar to that issue we worked through before.
So i need to put ‘caption’ and ‘attribution’ in the template/lib/api.js?
And make sure that you're passing that data to a component on your frontend.
i added ‘caption’ to the template/lib/api.js like this:

const postFields = `
  _id,
  name,
  title,
  excerpt,
  displayTitle,
  displayExcerpt,
  caption,
  attribution,
  'date': publishedAt,
  'slug': slug.current,
  'coverImage': mainImage, 
  'author': author->{name, 'picture': image.asset->url},
`;
and in the components/cover-image.js i added:


import cn from 'classnames';
import Link from 'next/link';
import { imageBuilder } from '../../lib/sanity';
import styles from './cover-image.module.scss';

export default function CoverImage({ title, caption, url, imageObject, slug }) {
  const image = (
    <img
      width={1020}
      height={654}
      alt={`Cover Image for ${title}`}
      className={cn('drop-shadow-sm', {
        'hover:drop-shadow-sm transition-shadow duration-200': slug,
      })}
      src={imageBuilder(imageObject).width(980).height(654).url()}
    />
  );

  return (
    <figure className='article-teaser default grid-col-12 skin-default'>
      {slug ? (
        <Link as={`/posts/${slug}`} href='/posts/[slug]'>
          <a aria-label={title}>{image}</a>
        </Link>
      ) : (
        image
      )}
      <figcaption className={styles.image_figcaption}>
        <div className={styles.caption_container}>
          <a>{caption}</a>
        </div>
      </figcaption>
    </figure>
  );
}
is it something i'm missing? Can't see the caption.
i added ‘caption’ to the template/lib/api.js like this:
Caption is under
mainImage
. So you likely need to add:
mainImage {
  caption, 
  attritbution
}
Then you would need to access
mainImage.caption
and
mainImage.attribution
in your components.
Like this?

const postFields = `
  _id,
  name,
  title,
  excerpt,
  displayTitle,
  displayExcerpt,
  'date': publishedAt,
  'slug': slug.current,
  'coverImage': mainImage, 
  mainImage {
  caption, 
  attribution
}
  'author': author->{name, 'picture': image.asset->url},
`;

Like this?

const postFields = `
  _id,
  name,
  title,
  excerpt,
  displayTitle,
  displayExcerpt,
  'date': publishedAt,
  'slug': slug.current,
  'coverImage': mainImage, 
  mainImage {
  caption, 
  attribution
}
  'author': author->{name, 'picture': image.asset->url},
`;

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?