How to pull image src from Sanity using React

45 replies
Last updated: May 17, 2022
Hey guys how to pull image src from sanity?
AI Update

To pull image src URLs from Sanity, you'll want to use the @sanity/image-url package. This is the standard way to generate optimized image URLs from your Sanity image assets.

Quick Setup:

First, install the package:

npm install @sanity/image-url
# or
pnpm add @sanity/image-url

Then create a helper function (usually in a utils file):

import imageUrlBuilder from "@sanity/image-url";
import { client } from "./client"; // your Sanity client

const builder = imageUrlBuilder(client);

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

Usage:

Now you can use it to generate image URLs from your Sanity image fields:

import { urlFor } from './utils/imageUrl';

// In your component
const imageUrl = urlFor(sanityImageObject).url();

// With transformations
const optimizedUrl = urlFor(sanityImageObject)
  .width(800)
  .height(600)
  .format('webp')
  .url();

The sanityImageObject is what you get from your GROQ query - it's typically an object with an asset reference, not just a string.

With Next.js Image:

If you're using Next.js, you'll need to configure the domain in your next.config.js:

module.exports = {
  images: {
    remotePatterns: [
      { protocol: "https", hostname: "cdn.sanity.io" },
    ],
  },
};

Then use it like:

<Image 
  src={urlFor(image).url()} 
  alt="Description"
  width={800}
  height={600}
/>

Pro tip: The image URL builder supports tons of transformations like quality settings, cropping, format conversion (including WebP and AVIF with .auto('format')), and more. Sanity's CDN automatically caches these transformed images globally for fast delivery.

import React, { useEffect, useState, useLayoutEffect } from 'react';

import { Link } from "react-router-dom";

import Header from "../components/Header";

import "./Home.css";

import {Carousel, Card, Image} from 'antd';

import {client} from "../lib/client";

import imageUrlBuilder from '@sanity/image-url'



const Home = () => {

const [products,setProducts]= useState([]);

const [bannerData,setBannerData]= useState([])


const builder = imageUrlBuilder(client)

function urlFor(source) {

const url= builder.image(source)

return url

}




useEffect(() => {
`client.fetch(
*[_type == "featured"]{

name,

genre,

image

}
).then((data)=&gt;setProducts(data))`

}, [])


useLayoutEffect(() => {
`client.fetch(
*[_type == "banner"]{

image

}
).then((data)=&gt;setBannerData(data))`
}, [])


return(

<>

<div className="container">

<Header/>

<Carousel autoplay className="carousel">

{bannerData?.map((data) => {

return <img src={urlFor(data.image.asset).url()} className="carousel-img" alt="carousel"></img>;

})}

</Carousel>

<div className="cards">

{products.map((product)=>{

return(

<Card className="card" key={product._id}>

<Link to="/categories" state={product.genre} className="link" style={{textDecoration:"none"}}>

<img src={urlFor(product.image.asset).url()} alt={product.name} className="card-content"/>

<h4>{product.name}</h4>

</Link>

</Card>

)

})}




{/*<Card className="card">*/}

{/*  <h1>Shop By Category</h1>*/}

{/*<div className="card-content">*/}

{/*  {catCard.map((e) => {*/}

{/*    return (*/}

{/*        <img*/}

{/*            src={e}*/}

{/*            alt="category"*/}

{/*            className="card-category"*/}

{/*            onClick={() => console.log("beauty")} key={e}*/}

{/*        ></img>*/}

{/*    );*/}

{/*  })}*/}

{/*  <br />*/}

{/*  <Link to="/" className="link">*/}

{/*    Shop All*/}

{/*  </Link>*/}

{/*</div>*/}

{/*</Card>*/}

</div>

</div>

</>

)



}

export default Home;
Not able to do it
How does your query look like? You kind of have to do it in the query
It is there is useEffect
the query
Everything else seems to be working except for image src
Yeah images are a reference type, so you have to do some fancy things like add -&gt;{...} to get access to the asset
I'm kind of new myself, but I basically did image{asset-&gt;{url}}
In the query?
Yes, if you try that in the invision part of sanity studio
And then show me what sanity gives u back
user S
thanks a lot it works now
No problem! Also if you want to understand it more, you can try and break it down in the incision part
Now it started throwing this error
It is displaying image initially but it suddenly throws error and image stops showing
Query looks like  client.fetch(`*[_type == "banner"]{
      image:image.asset->url
    }`).then((data)=>setBannerData(data))
  }, [])
user S
Please help me. First time using sanity and the image is not displaying
You have to adjust your query, so it’s
*[_type == "banner"] {
  "image": image.asset->url
}

ClientError {response: {…}, statusCode: 400, responseBody: '{\n "error": {\n "description": "unable to parse… "start": 64,\n "type": "queryParseError"\n }\n}', details: {…}, message: 'unable to parse entire expression', …}
     {      name: 'image',
            title: 'Image',
            type: 'array',
            of: [{ type: 'image' }],
            options: {
                hotspot: true,
            }
        },
This is my image schema
Hm, I think I’d refactor the code to something like this:
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import Header from "../components/Header";
import "./Home.css";
import { Carousel, Card, Image } from "antd";
import { client } from "../lib/client";

import imageUrlBuilder from "@sanity/image-url";
const urlFor = (source) => imageUrlBuilder(client).image(source);

const query = `{
  "featured": *[_type == "featured"]{name, genre, image},
  "banner": *[_type == "banner"]{image}
}`;


const Home = () => {
  const [products, setProducts] = useState([]);
  const [bannerData, setBannerData] = useState([]);
  useEffect(() => {
    client
      .fetch(query)
      .then(({ featured, banner }) => {
        setProducts(featured);
        setBannerData(banner);
      })
      .catch((error) => console.log(error));
  }, []);

  return (
    <>
      <div className="container">
        <Header />
        <Carousel autoplay className="carousel">
          {bannerData?.map((data) => {
            return (
              <img
                src={urlFor(data.image).url()}
                className="carousel-img"
                alt="carousel"
              ></img>
            );
          })}
        </Carousel>
        <div className="cards">
          {products.map((product) => {
            return (
              <Card className="card" key={product._id}>
                <Link
                  to="/categories"
                  state={product.genre}
                  className="link"
                  style={{ textDecoration: "none" }}
                >
                  <img
                    src={urlFor(product.image).url()}
                    alt={product.name}
                    className="card-content"
                  />
                  <h4>{product.name}</h4>
                </Link>
              </Card>
            );
          })}
        </div>
      </div>
    </>
  );
};
export default Home;

The image-url library only needs the top-level
image
field to generate the urls. That will also make hotspot and crop work if you have that enabled. And you can use its methods to control dimensions and other things. • I’ve combined the queries into one, so that you don’t need to fetch 2 times. Also,
useLayoutEffect
will block your DOM rendering. Not sure if that’s the intention, but I’d generally stay clear of it (also, you should probably look into a framework that’s able to server render this if you’re planning to run this on the web. Next.js is a good choice)• I haven’t actually run this code, but let me know if it works, or what errors you get.
Error: Unable to resolve image URL from source ([{"_key":"c86fb91a8ad3","_type":"image","asset":{"_ref":"image-48f1b8fa7db78f55843dcd58547080123dc36adb-192x293-webp","_type":"reference"}}]) at urlForImage (urlForImage.ts
:43:1) at ImageUrlBuilder.url (builder.ts
:229:1) at Home.js
:56:1 at Array.map (&lt;anonymous&gt;)
at Home (Home.js
:45:1) at renderWithHooks (react-dom.development.js
:14985:1) at updateFunctionComponent (react-dom.development.js
:17356:1) at beginWork (react-dom.development.js
:19063:1) at HTMLUnknownElement.callCallback (react-dom.development.js
:3945:1) at Object.invokeGuardedCallbackDev (react-dom.development.js
:3994:1)
oh. of course. this is an array of images?
Yes sir
Not sure if this is what you want, but then you have to map over
image

 <Carousel autoplay className="carousel">
          {bannerData?.map((data) => {
            return data.image.map(img => (
              <img
                key={img._key}
                src={urlFor(img).url()}
                className="carousel-img"
                alt="carousel"
              ></img>
            ));
          })}
        </Carousel>
I’d refactor this field:
   {      name: 'image',
            title: 'Image',
            type: 'array',
            of: [{ type: 'image' }],
            options: {
                hotspot: true,
            }
        },
to

{
  name: 'images',
  title: 'Images',
  type: 'array',
  of: [{ 
    type: 'image', 
    options: {
      hotspot: true,
    } 
  }],
},

I get the same error even after mapping
Can you DM me your project ID?
Yes sure
I DMed you my project id
Thanks. It’s bit easier when you actually see the data 🙂
Any idea why I am getting error even after mapping img
Same error
import React, { useEffect, useState } from "react";
import imageUrlBuilder from "@sanity/image-url";
import { client } from "../lib/client";


const urlFor = (source) => imageUrlBuilder(client).image(source);

const query = `{
  "featured": *[_type == "featured"]{name, genre, image},
  "banner": *[_type == "banner"][0]{image}
}`;


const Home = () => {
  const [products, setProducts] = useState([]);
  const [bannerData, setBannerData] = useState([]);
  useEffect(() => {
    client
      .fetch(query)
      .then(({ featured, banner }) => {
        console.log({ featured, banner });
        setProducts(featured);
        setBannerData(banner);
      })
      .catch((error) => console.log(error));
  }, []);

  return (
    <>
      <div className="container">
        <div autoplay className="carousel">
          <img
            src={urlFor(bannerData.image).url()}
            className="carousel-img"
            alt="carousel"
          ></img>
        </div>
        <div className="cards">
          {products.map((product) => {
            return (
              <div className="card" key={product._id}>
                {
                  product.image &&
                  product.image.map(img => (
                    <img
                      key={img._key}
                      src={urlFor(img).url()}
                      alt={product.name}
                      className="card-content"
                    />
                  ))
                }
                <h4>{product.name}</h4>
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
};
export default Home;
This works for me (I stripped away your components)
It was the products docs which had the images with arrays
So
product.image
is an array of images. While
banner.image
is not
Now it urlForImage.ts:43 Uncaught Error: Unable to resolve image URL from source (undefined) at urlForImage (urlForImage.ts
:43:1) at ImageUrlBuilder.url (builder.ts
:229:1) at Home (Home.js
:43:1) at renderWithHooks (react-dom.development.js
:14985:1) at mountIndeterminateComponent (react-dom.development.js
:17811:1) at beginWork (react-dom.development.js
:19049:1) at HTMLUnknownElement.callCallback (react-dom.development.js
:3945:1) at Object.invokeGuardedCallbackDev (react-dom.development.js
:3994:1) at invokeGuardedCallback (react-dom.development.js
:4056:1) at beginWork$1 (react-dom.development.js
:23964:1)
urlForImage.ts:43 Uncaught Error: Unable to resolve image URL from source (undefined) at urlForImage (urlForImage.ts
:43:1) at ImageUrlBuilder.url (builder.ts
:229:1) at Home (Home.js
:36:1) at renderWithHooks (react-dom.development.js
:14985:1) at mountIndeterminateComponent (react-dom.development.js
:17811:1) at beginWork (react-dom.development.js
:19049:1) at HTMLUnknownElement.callCallback (react-dom.development.js
:3945:1) at Object.invokeGuardedCallbackDev (react-dom.development.js
:3994:1) at invokeGuardedCallback (react-dom.development.js
:4056:1) at beginWork$1 (react-dom.development.js
:23964:1)
What does your code look like now?
import React, { useEffect, useState } from "react";

import { Link } from "react-router-dom";

import Header from "../components/Header";

import "./Home.css";

import { Carousel, Card, Image } from "antd";

import { client } from "../lib/client";


import imageUrlBuilder from "@sanity/image-url";

const urlFor = (source) => imageUrlBuilder(client).image(source);

`const query =
{

"featured": *[_type == "featured"]{name, genre, image},

"banner": *[_type == "banner"][0]{image}
`}`;`



const Home = () => {

const [products, setProducts] = useState([]);

const [bannerData, setBannerData] = useState([]);

useEffect(() => {

client

.fetch(query)

.then(({ featured, banner }) => {

console.log({ featured, banner });

setProducts(featured);

setBannerData(banner);

})

.catch((error) => console.log(error));

}, []);


return (

<>

<Header/>

<div className="container">

<Carousel autoplay className="carousel">

<img

src={urlFor(bannerData.image).url()}

className="carousel-img"

alt="carousel"

></img>

</Carousel>

<div className="cards">

{products.map((product) => {

return (

<Card className="card" key={product._id}>

<Link>

{

product.image &&

product.image.map(img => (

<img

key={img._key}

src={urlFor(img).url()}

alt={product.name}

className="card-content"

/>

))

}

<h4>{product.name}</h4>

</Link>

</Card>

);

})}

</div>

</div>

</>

);

};

export default Home;
Unrelated: When posting code blocks, please use triple backticks fences. Like this:
This is a 
multiline
code snippet
Yeah, sorry. It’s so easy to miss things when I don’t run the code. So the reason this isn’t working is that we’re setting
bannerData
to be an empty array by default. Looking through your code it seems to me that maybe you want to rethink your data modelling a bit as well? Since you have a
Carousel
you probably want
banner
to have an array of images, and not just the one image it has now? Anyways, given the data structures you have now, I think this code should work.

const query = `{
  "featured": *[_type == "featured"]{name, genre, image},
  "banner": *[_type == "banner"][0]{image} // [0] will get the banner document with the last update
}`;

const Home = () => {
  const [products, setProducts] = useState([]);
  const [bannerData, setBannerData] = useState({}); // CHANGE THIS
  useEffect(() => {
    client
      .fetch(query)
      .then(({ featured, banner }) => {
        setProducts(featured);
        setBannerData(banner);
        //console.log({ featured, banner });
      })
      .catch((error) => console.log(error));
  }, []);
  return (
    <>
      <Header />
      <div className="container">
        {/* CHANGE FROM HERE */}
        {bannerData?.image && <Carousel autoplay className="carousel">
          <img
            src={urlFor(bannerData.image).url()}
            className="carousel-img"
            alt="carousel"
          ></img>
        </Carousel>}
        {/* …TO HERE */}
        <div className="cards">
          {products.length > 0 && products.map((product) => {
            return (
              <Card className="card" key={product._id}>
                <Link>
                  {
                    product.image &&
                    product.image.map(img => (
                      <img
                        key={img._key}
                        src={urlFor(img).url()}
                        alt={product.name}
                        className="card-content"
                      />
                    ))
                  }
                  <h4>{product.name}</h4>
                </Link>
              </Card>
            );
          })}
        </div>
      </div>
    </>
  );
};

Instead of a document type named
banner
I’d probably make a
siteSettings
or a
page
type with an object called
banner
that has an array of
image
called
images
in it.
Thanks a lot
user S
. You solved my painful issue

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?