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?
May 17, 2022, 5:19 AM
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)=>setProducts(data))`

}, [])


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

image

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

May 17, 2022, 6:15 AM
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', …}
May 17, 2022, 6:29 AM
     {      name: 'image',
            title: 'Image',
            type: 'array',
            of: [{ type: 'image' }],
            options: {
                hotspot: true,
            }
        },
May 17, 2022, 6:35 AM
This is my image schema
May 17, 2022, 6:36 AM
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.
May 17, 2022, 6:39 AM
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)
May 17, 2022, 6:42 AM
oh. of course. this is an array of images?
May 17, 2022, 6:43 AM
Yes sir
May 17, 2022, 6:43 AM
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>
May 17, 2022, 6:45 AM
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,
    } 
  }],
},

May 17, 2022, 6:47 AM
I get the same error even after mapping
May 17, 2022, 6:48 AM
Can you DM me your project ID?
May 17, 2022, 6:48 AM
Yes sure
May 17, 2022, 6:49 AM
I DMed you my project id
May 17, 2022, 6:57 AM
Thanks. It’s bit easier when you actually see the data 🙂
May 17, 2022, 6:57 AM
Any idea why I am getting error even after mapping img
May 17, 2022, 6:57 AM
Same error
May 17, 2022, 6:57 AM
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)
May 17, 2022, 6:57 AM
It was the products docs which had the images with arrays
May 17, 2022, 6:58 AM
So
product.image
is an array of images. While
banner.image
is not
May 17, 2022, 6:58 AM
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)
May 17, 2022, 7:01 AM
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)
May 17, 2022, 7:10 AM
What does your code look like now?
May 17, 2022, 7:17 AM
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;
May 17, 2022, 7:19 AM
Unrelated: When posting code blocks, please use triple backticks fences. Like this:
This is a 
multiline
code snippet
May 17, 2022, 8:04 AM
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>
    </>
  );
};

May 17, 2022, 8:10 AM
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.
May 17, 2022, 8:11 AM
Thanks a lot
user S
. You solved my painful issue
May 17, 2022, 8:31 AM

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?