Help with outputting background image from Sanity in Next.js project

4 replies
Last updated: Jun 25, 2021
Hey amigs!I’m putting together my first Sanity project (using Next.js for the fe) and am having some trouble outputting a background image from Sanity…any help would be much appreciated
🙂

The story so far…• I have 3 documents in my schema which have the following types:
projects
,
articles
&
people

people
and
projects
are referenced in
articles
• I would like to output the image from
people
as a background image on a
.com/projects
and/or
.com/projects/[slug]
page• My groq query (which lives in
lib/queries.js
) looks like this:
 export const projectQuery = `
*[_type == "project"] | order(slug.current asc) {
  _id,
    website,
    slug,
    image,
    title,
    description,
  "collaborators": *[_type == "article" && references(^._id)]| order(publishedAt desc){
    personReference->{
      image{asset->{url,_id}}
  	}
  }[0...3],
}`;
• I then have an
<Avatar />
component that loos like this
import { urlFor } from "../lib/sanity";

const Avatar = (props) => {
  return (
    <>
      <StyledAvatar
        css={{
          backgroundImage: `url(${urlFor(props.url).url()})`,
        }}
      />
    </>
  );
};
export default Avatar;
• Which is used in a
<ProjectCard />
component that…
import { sanityClient } from "../../lib/sanity";
import Avatar from "../avatar";

const ProjectCard = (props) => {
  const {
    _id,
    website,
    slug,
    image,
    title,
    description,
    collaborators,
  } = props;

  const collaboratorsImageItemProps = props.collaborators;

  return (
    <>
          {collaboratorsImageItemProps.length ? (
            <>
              <FlexBox>
                <Label>Collaborators</Label>
                <FlexAvatars>
                  {collaborators &&
                    collaborators.map((props) => (
                      <Avatar key={props._id} url={props.url} />
                    ))}
                </FlexAvatars>
              </FlexBox>
            </>
          ) : (
            <Label>No collaborators</Label>
          )}
    </>
  );
};

export default ProjectCard;
• Which is finally used in
index.js

import { sanityClient, urlFor } from "../../lib/sanity";
import { projectQuery } from "../../lib/queries";
import ProjectCard from "../../components/cards/project-card";

export default function ProjectsIndex({ projects }) {
  return (

        <>
          {projects && (
            <Grid>
              {projects.map((project) => (
                <ProjectCard
                  key={project._id}
                  website={project.website}
                  image={project.image}
                  title={project.title}
                  slug={project.slug}
                  description={project.description}
                  collaborators={project.collaborators}
                />
              ))}
            </Grid>
          )}
        </>

  );
}

export async function getStaticProps() {
  const projects = await sanityClient.fetch(projectQuery);

  return {
    props: { projects },
  };
}
Thanks in advance
🙌
AI Update

Looking at your code, I can spot the issue! In your GROQ query, you're correctly fetching the image data with personReference->{image{asset->{url,_id}}}, but when you're mapping over the collaborators in your <ProjectCard /> component, you're not accessing the nested structure properly.

The problem is here:

{collaborators.map((props) => (
  <Avatar key={props._id} url={props.url} />
))}

Your query returns collaborators with a structure like:

{
  personReference: {
    image: {
      asset: {
        url: "...",
        _id: "..."
      }
    }
  }
}

But you're trying to access props.url directly, which doesn't exist at that level.

Here's the fix:

Update your mapping in <ProjectCard /> to properly access the nested image:

{collaborators.map((collaborator) => (
  <Avatar 
    key={collaborator.personReference._id} 
    url={collaborator.personReference.image} 
  />
))}

Then in your <Avatar /> component, pass the entire image object to urlFor:

const Avatar = (props) => {
  return (
    <StyledAvatar
      css={{
        backgroundImage: `url(${urlFor(props.url).url()})`,
      }}
    />
  );
};

The urlFor() function from @sanity/image-url is smart enough to handle the image object structure (with the asset reference) and build the proper CDN URL.

Alternative approach - you could also simplify your GROQ query to flatten the structure:

"collaborators": *[_type == "article" && references(^._id)]| order(publishedAt desc){
  "personImage": personReference->image,
  "personId": personReference->_id
}[0...3]

Then access it like:

{collaborators.map((collaborator) => (
  <Avatar 
    key={collaborator.personId} 
    url={collaborator.personImage} 
  />
))}

Either approach should work! The key is making sure you're passing the complete image object (with the asset reference) to urlFor(), which will handle building the optimized CDN URL for you.

Hey
user M
Thanks for your assistance on this. Although this is successful in outputting a BG image, it unfortunately outputs the main project image 3 times instead of the image for the people we reference the project. Could this be because we are using
image
the card which clashes with the main project image? Or perhaps my implementation of the card in the index page?
Ah, I think it should be the following, otherwise it will just use the main image:
{collaborators &&
   collaborators.map(collaborator => (
     <Avatar key={props._id} 
        image={collaborator.personReference.image} 
     />
   ))}
AMAZING, it works! Thanks a milli, Racheal :fast_parrot:
Perfect! Happy to help!

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?