I'm unable to render a dynamic image coming from strapi cms using GatsbyImage from gatsby-plugin-image
Everything worked fine with the old gatsby-image plugin, really don't know what i'm doing wrong here.
Warning: Failed prop type: The prop srcis marked as required inJ, but its value is undefined.
How should i use the image object or the getImage() function to render the GatsbyImage ?
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
title
image {
localFile {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
import { GatsbyImage, getImage, StaticImage } from "gatsby-plugin-image";
const Project = ({
image,
}: any) => {
const gatsbyImg = getImage(image);
const imgPath = image.localFile.childImageSharp.fluid;
console.log("gatsbyImg:", gatsbyImg);
console.log("image:", image);
console.log("imgPath:", imgPath);
return (
...
<GatsbyImage image={imgPath} alt={"alt"} />
)
Output result for the console logs above:
As you pointed out, the issue appears because of the mix of GraphQL nodes between gatsby-image and gatsby-plugin-image. Summarizing a lot, your queryable node should be gatsyImageData instead of fluid or fixed.
This is the previous syntax:
import { graphql } from "gatsby"
export const query = graphql`
{
file(relativePath: { eq: "images/example.jpg" }) {
childImageSharp {
fixed {
...GatsbyImageSharpFixed
}
}
}
}
`
While the new one looks like:
import { graphql } from "gatsby"
export const query = graphql`
{
file(relativePath: { eq: "images/example.jpg" }) {
childImageSharp {
gatsbyImageData(layout: FIXED)
}
}
}
`
Check further details in the migration guide
The problem is that you are querying the old GraphQL node (fluid or fixed) that worked for Img (from gatsby-image) while the new component, GatsbyImage requires an image prop, extracted from gatsbyImageData node.
That said, getImage is a helper function (not mandatory) that helps you to clean up the code.
Double-check the following steps:
Install the dependencies:
npm install gatsby-plugin-image gatsby-plugin-sharp gatsby-transformer-sharp
Remove the old gatsby-image dependency references if any.
Clean the cache
Query the proper nodes: check them in localhost:8000/___graphiql
(Optional) run the codemod:
npx gatsby-codemods gatsby-plugin-image <optional-path>
This will automatically adapt your old code to the new syntax.
In the end, your code should look like:
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
title
image {
localFile {
childImageSharp {
gatsbyImageData(layout: FIXED)
}
}
}
}
}
Your component should work alone since now is not working because it's not getting the proper data.
Related
am new to gatsby and graphql and I came across a tutorial where it is mentioned to fetch all the data using .map. But I want to fetch only one element from the DB. So how do I do it?
import React from "react";
import Layout from "../components/layout";
import { useStaticQuery, graphql, Link } from "gatsby";
const Blogs = () => {
const data = useStaticQuery(
graphql`
query {
allMarkdownRemark(sort: { frontmatter: { date: ASC } }) {
edges {
node {
frontmatter {
title
date(formatString: "DD MM, YYYY")
}
excerpt
id
fields {
slug
}
}
}
}
}
`
);
return (
<Layout>
<ul>
{data.allMarkdownRemark.edges.map((edge) => {
return (
<li key={edge.node.id}>
<h2>
<Link to={`/blog/${edge.node.fields.slug}/`}>
{edge.node.frontmatter.title}
</Link>
</h2>
<div>
<span>
Posted on {edge.node.frontmatter.date}
</span>
</div>
<p>{edge.node.excerpt}</p>
<div>
<Link to={`/blog/${edge.node.fields.slug}/`}>Read More</Link>
</div>
</li>
);
})}
</ul>
</Layout>
);
};
export default Blogs;
Lets say I have multiple blogs and I wish to show only a specific one in a page through query like...
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
How to get this on a page to display?
Thanks in advance!
Depending on what do you want to achieve:
If you want just a specific single post. You can filter your useStaticQuery to add the value of the id (if you know it beforehand) like:
query MyQuery {
markdownRemark((id: {eq: "123"}) {
title
description
content
}
}
useStaticQuery as the name points out, is static and doesn't accept dynamic values.
Another alternative is to get a specific position from data.allMarkdownRemark to display it.
If you just want a single post without any filter you can take advantage of the GraphQL query options:
{
allMarkdownRemark(limit: 1) {
edges {
node {
frontmatter {
title
}
}
}
}
}
If you are trying to create dynamic posts, hence each post template will display a different blog post (one per template), you need to pass a filter value from gatsby-node.js (where you create the post pages) to the template through Gatsby's context:
// gatsby-node.js
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/blog-post.js`),
context: {
id: node.id,
title: node.title
},
})
})
Note: here I'm lifting the id and the title. Use whatever works better for you
Now, you can take advantage of the context in your Blogs component (as long as it's a template):
const Blogs = ({data}) => {
console.log("your blog post is:", data)
return (
<Layout>
<h1>{data.markdownRemark.title}</h1>
</Layout>
);
};
export const query = graphql`
query($id: String!, $title: String!) {
markdownRemark((id: {eq: $id}) {
title
description
content
}
}
`
export default Blogs;
In other words: the first approach uses a static query (via useStaticQuery hook. Static, no dynamic parameters allowed) and the second uses a page query (only available in pages or templates)
With your query:
query MyQuery {
markdownRemark((id: {eq: "9ac19d6d"}) //Some ID {
title
description
content
}
}
Your data will be in data.markdownRemark
You can access those 3 fields directly.
const { title, description, content ] = data.markdownRemark;
return (
<Layout>
<div>
<p>{title}</p>
<p>{description]</p>
<p>{content}</p>
</div>
</Layout>
)
I'm working on the blog based on React, TS and Gatsby.
Blog posts are based on markdown. Each blog post will have a similar header with title, time necessary to read the article and the logos of the technologies that the particular blog post will be about.
I have a problem with rendering those images dynamically. My idea was to create something like this in markdown
---
path: "/fourth"
date: "2021-06-02"
title: "TypeScript - intro"
readTime: "140"
author: "Adam Kniec"
imgs: [typescript, react]
---
That's the fourth blog post
after that I wanted to create a graphql query and get the imgs names like so:
export const postQuery = graphql`
query BlogPostByPath($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
path
readTime
title
author
imgs
date
}
}
}
`;
I have the array of images in the props now and I wanted to render those images like this
{data.markdownRemark.frontmatter.imgs.map((imgPath) => {
const imgPatha = `../images/${imgPath}`;
return <StaticImage src={imgPatha} alt="asdasd" />;
})}
Unfortunately, gatsby gives me the warning
react_devtools_backend.js:2560 Image not loaded ../images/typescript
Is this the correct approach ? Please let me know what I'm doing wrong or how to render those images dynamically.
As it has been said by #coreyward you can't use dynamic props in the StaticImage component, it's a known restriction.
That said, you have two choices:
Using the standard img HTML tag.
Using the GatsbyImage component. To do it, you'll need to add the images in your filesystem to allow Gatsby to create the proper nodes and then, you will need to query them in your pages/templates. Without further details on the implementation, it's impossible to guess how your code should look like but the idea relies on something like:
import { graphql } from "gatsby"
import { GatsbyImage, getImage } from "gatsby-plugin-image"
function BlogPost({ data }) {
const image = getImage(data.blogPost.avatar)
return (
<section>
<h2>{data.blogPost.title}</h2>
<GatsbyImage image={image} alt={data.blogPost.author} />
<p>{data.blogPost.body}</p>
</section>
)
}
export const pageQuery = graphql`
query {
blogPost(id: { eq: $Id }) {
title
body
author
avatar {
childImageSharp {
gatsbyImageData(
width: 200
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
`
I'm building a website with gatsby and Strapi and i'm facing an issue that can't fix.
Here's my problem:
I made a custom graphQL Schema to avoid building fails if my clients leave empty fields in the CMS. But since i did that for my homepage banner, childImageSharp is null.
I'm assuming i did my custom graphql schemas wrong but I can't understand where.
Here's my files:
Homepage.js
const IndexPage = ({ data }) => {
return (
<Layout>
{data.allStrapiBanner.edges[0].node.banner.childImageSharp.fixed &&
<Img
fixed={data.allStrapiBanner.edges[0].node.banner.childImageSharp.fixed}
imgStyle={{ position: "static" }}
/>
}
</Layout>
)
}
export default IndexPage
export const pageQuery = graphql`
allStrapiBanner {
edges {
node {
banner {
childImageSharp {
fixed(width: 800){
src
}
}
}
}
}
}
}
`
Here's my gatsby-node.js custom Schema:
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type allStrapiBanner implements Node {
banner: childImageSharp
}
type childImageSharp{
childImageSharp: fixed
}
type fixed{
fixed(width: Int): src
}
type src{
src: String
}
`
createTypes(typeDefs)
}
Now gatsby build without error when i don't upload an image to my banner field but when i upload an image childImageSharp is null
I already read the doc (https://www.gatsbyjs.com/docs/reference/graphql-data-layer/schema-customization/) but i can't manage to make it work.
if someone can give me a tips to how to correctly create my custom schemas for childImageSharp.
Thanks you very much in advance
SOLVED
After a lot of hours of brute force trying to understand what was wrong, I have finally fixed my problem. I can't believe it was so simple:
I simply changed my schema in gastby-node.js to
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type allStrapiBanner implements Node {
banner: File
}
`
createTypes(typeDefs)
}
and it work
I am working on a Gatsby website, and I keep getting "TypeError: Cannot read property 'childImageFluid' of undefined"
The code I have is this in my Project.js file
import React from "react"
import PropTypes from "prop-types"
import Image from "gatsby-image"
import { FaGithubSquare, FaShareSquare } from "react-icons/fa"
const Project = ({description, title, github, stack, url, image, index}) => {
return (
<article className="project">
<Image fluid={image.childImageFluid.fluid} className="project-img" />
</article>
)
}
Project.propTypes = {}
export default Project
and I have the graphql set up in the index.js file where it will be displayed, and everything is working as it should in graphql...
export const query = graphql`
{
allStrapiProjects(filter: { featured: { eq: true } }) {
nodes {
github
id
description
title
url
image {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
stack {
id
title
}
}
}
}
`
everything up to the what I am working on in the Project.js file is in my github - https://github.com/matthewbert86/gatsby-site but all of that code is in the first code section above.
When you use a page query in GraphQL, your gathered data is stored inside a data object (as a props). You need to iterate through it until you get your fluid image. It should be in: props.data.allStrapiProjects.nodes.image.childImageFluid.fluid. Since you are destructuring everything in your <Project> component:
const Project = ({ data }) => {
let { description, title, github, stack, url, image } = data.allStrapiProjects.nodes; // your data is here
return (
<article className="project">
<Img fluid={data.allStrapiProjects.nodes.image.childImageFluid.fluid} className="project-img" />
</article>
)
}
After destructuring, you can refactor it to:
<Img fluid={image.childImageFluid.fluid} className="project-img" />
Another point, I guess that the gatsby-image import should be Img, not Image.
I am currently trying to create a blog site using gatsbyjs, and would like my blog posts to have their pages created for them programmatically.
I followed the steps in the tutorials section of the official documentation, but instead of creating the pages from a markdown file, I am pulling data from the contentful CMS. I keep on hitting this block whenever I run my code.
gatsby-node.js
const path= require('path')
exports.createPages=({graphql,actions})=>{
const {createPage}=actions
const blogPost= path.resolve('./src/components/blogComponents/blog-post.js')
return new Promise((resolve,reject)=>{
graphql(`
{
allContentfulBlog{
edges{
node{
slug
}
}
}
}
`).then(results=>{
// console.log(results)
if(results.error){
reject(results.error)
}
// create blog post pages
const posts=results.data.allContentfulBlog.edges
posts.forEach((post,index)=>{
console.log(`showing slugs: ${post.node.slug}`)
createPage({
path:post.node.slug,
component:blogPost ,
context:{
slug:post.node.slug,
}
})
})
}).then(resolve)
})
}
blog-post.js
import React from 'react'
import { graphql} from 'gatsby'
const blogPost=({data})=>{
return (
<div>
new page created
</div>
)
}
export default blogPost
export const pageQuery= graphql`
query ($slug: String!){
allContentfulBlog (slug:{eq: $slug }) {
edges{
node{
content{
content
}
}
}
}
}
`
I expected the blog-post.js file to receive all appropriate variables, but instead, it kept on throwing the "unknown argument 'slug' " error while making reference to the blog-post.js file.
In blog-post.js, it looks like you might have been filtering the posts incorrectly; either do this:
export const pageQuery= graphql`
query ($slug: String!){
- allContentfulBlog (slug:{eq: $slug }) {
+ allContentfulBlog (filter: { slug: { eq: $slug } })
edges {
node {
content {
content
}
}
}
}
}
`
Or this:
export const pageQuery= graphql`
query ($slug: String!){
- allContentfulBlog (slug:{eq: $slug }) {
+ ContentfulBlog (slug:{eq: $slug }) {
content {
content
}
}
}
`