How to pull image src from Sanity using React
45 replies
Last updated: May 17, 2022
M
Hey guys how to pull image src from sanity?
May 17, 2022, 5:19 AM
M
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
M
Not able to do it
May 17, 2022, 5:24 AM
D
How does your query look like? You kind of have to do it in the query
May 17, 2022, 5:25 AM
M
It is there is useEffect
May 17, 2022, 5:26 AM
M
the query
May 17, 2022, 5:26 AM
M
Everything else seems to be working except for image src
May 17, 2022, 5:26 AM
D
Yeah images are a reference type, so you have to do some fancy things like add ->{...} to get access to the asset
May 17, 2022, 5:28 AM
D
I'm kind of new myself, but I basically did image{asset->{url}}
May 17, 2022, 5:28 AM
M
In the query?
May 17, 2022, 5:29 AM
D
Yes, if you try that in the invision part of sanity studio
May 17, 2022, 5:29 AM
D
And then show me what sanity gives u back
May 17, 2022, 5:30 AM
M
user S
thanks a lot it works nowMay 17, 2022, 5:30 AM
D
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
M
Now it started throwing this error
May 17, 2022, 5:43 AM
M
It is displaying image initially but it suddenly throws error and image stops showing
May 17, 2022, 5:43 AM
M
Query looks like client.fetch(`*[_type == "banner"]{ image:image.asset->url }`).then((data)=>setBannerData(data)) }, [])
May 17, 2022, 5:45 AM
M
user S
Please help me. First time using sanity and the image is not displayingMay 17, 2022, 5:52 AM
S
You have to adjust your query, so it’s
*[_type == "banner"] { "image": image.asset->url }
May 17, 2022, 6:15 AM
M
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
M
{ name: 'image', title: 'Image', type: 'array', of: [{ type: 'image' }], options: { hotspot: true, } },
May 17, 2022, 6:35 AM
M
This is my image schema
May 17, 2022, 6:36 AM
Hm, I think I’d refactor the code to something like this:
•
The image-url library only needs the top-level
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
imagefield 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,
useLayoutEffectwill 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
M
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 (<anonymous>)
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)
:43:1) at ImageUrlBuilder.url (builder.ts
:229:1) at Home.js
:56:1 at Array.map (<anonymous>)
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
M
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:
to
{ name: 'image', title: 'Image', type: 'array', of: [{ type: 'image' }], options: { hotspot: true, } },
{ name: 'images', title: 'Images', type: 'array', of: [{ type: 'image', options: { hotspot: true, } }], },
May 17, 2022, 6:47 AM
M
I get the same error even after mapping
May 17, 2022, 6:48 AM
M
Yes sure
May 17, 2022, 6:49 AM
M
I DMed you my project id
May 17, 2022, 6:57 AM
M
Any idea why I am getting error even after mapping img
May 17, 2022, 6:57 AM
M
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;
May 17, 2022, 6:57 AM
M
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)
: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
M
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)
: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
M
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
K
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
bannerDatato 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
Carouselyou probably want
bannerto 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
bannerI’d probably make a
siteSettingsor a
pagetype with an object called
bannerthat has an array of
imagecalled
imagesin it.
May 17, 2022, 8:11 AM
M
Thanks a lot
user S
. You solved my painful issueMay 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.