Output referenced person image as background in Next.js from Sanity query
🙂
The story so far…• I have 3 documents in my schema which have the following types:
projects,
articles&
people•
peopleand
projectsare referenced in
articles• I would like to output the image from
peopleas a background image on a
.com/projectsand/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],
}`;<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;<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;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 },
};
}🙌
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.
Show original thread4 replies
Was this answer helpful?
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.