Reusable Gatsby Transformer Cloudinary Image dynamic image sources - reactjs

I've installed "gatsby-transformer-cloudinary" to my gatsby website. I've implemented API integration and It can be fetched and I can see any single image on a page from Cloudinary. I just want to use this component dynamically and I need your help how do I used image name area as dynamically like props ((eg: "image"))?
import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import Image from "gatsby-image"
export default (props) => {
const data = useStaticQuery(graphql`
query {
image: file(name: { eq: "3144_xl-2015" }) {
cloudinary: childCloudinaryAsset {
fixed(width: 300) {
...CloudinaryAssetFixed
}
}
}
}
`)
return (
<div className="image-example">
<Image
fixed={data.image.cloudinary.fixed}
alt={props.alt}
title={props.title}
/>
</div>
)
}

You are using a staticQuery (or useStaticQuery hook, in the end it works exactly in the same way), since it's a limitation from it, you can't pass variables. From the documentation:
StaticQuery does not accept variables (hence the name “static”), but
can be used in any component, including pages
If you want to use a dynamic <Img> component from gatsby-image you will need to use a page query and pass some kind of unique value (like a slug) and filter through it.

Related

How can I dynamically add images to my pages in Gatsby using the image component?

So I have the image component that renders 2 different images, but I want to be able to just add my image component to any page and just pass in the specific image as a prop instead of having to hard code a new query for each image I need to use.
If I had 50 images, I'd like to just pass image-50.jpg in as a prop instead of making a specific query for it. Is there a way to do that with graphql in gatsby?
Here is my current image component code
import { graphql, useStaticQuery } from "gatsby"
import Img from "gatsby-image"
import React from "react"
const Image = () => {
const data = useStaticQuery(graphql`
query {
astronaut: file(relativePath: { eq: "gatsby-astronaut.png" }) {
childImageSharp {
fluid(maxHeight: 600) {
...GatsbyImageSharpFluid
}
}
}
person: file(relativePath: { eq: "profile.jpg" }) {
childImageSharp {
fluid(maxHeight: 600) {
...GatsbyImageSharpFluid
}
}
}
}
`)
return (
<>
<Img fluid={data.astronaut.childImageSharp.fluid} />
<Img fluid={data.person.childImageSharp.fluid} />
</>
)
}
export default Image
Is there a way to just add the image dynamically and then render it to a new page?
Something like this <Image src={"profile.jpg"} />
I don't know how I could add that to the graphql and imagine I have 50 images, then I would have to either map through all 50 or manually add each query and that doesn't make sense
Believe it or not you cannot create a fully dynamic image component using a gastby-image without risking a (possibly very large) bloat in your bundle size. The problem is that static queries in Gatsby do not support string interpolation in it's template literal. You would need to search through all the files each time you use the component.
There are some solutions you can try in an existing SO post found here.
You can always use graphql fragments and write something like the below for your queries and then conditionally render the proper image based on a file name passed via props in your Image component but alas this also pretty clunky:
export const fluidImage = graphql`
fragment fluidImage on File {
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
`;
export const data = graphql`
query {
imageOne: file(relativePath: { eq: "one.jpg" }) {
...fluidImage
}
imageTwo: file(relativePath: { eq: "two.jpg" }) {
...fluidImage
}
imageThree: file(relativePath: { eq: "three.jpg" }) {
...fluidImage
}
}
`
// accessed like this
<Img fluid={data.imageOne.childImageSharp.fluid} />
// or this
<Img fluid={data.imageTwo.childImageSharp.fluid} />
// or this, dynamically (if you had a prop called imageName)
<Img fluid={data.[`${props.imageName}`].childImageSharp.fluid} />
As Apena's answer explains, it's tricky to work like that with Gatsby's Image. However, I must say that you can bypass it in different ways depending on the filesystem used and how the data is structured.
Keep in mind that if you set properly the filesystem in your gatsby-config.js, you are allowing Gatsby to recognize and to find all your images in your project, making them queryable and allowing them to be used by Gatsby Image component.
const path = require(`path`)
module.exports = {
plugins: [
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: path.join(__dirname, `src`, `images`),
},
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
],
}
You can find much better ways than querying each image in a staticQuery filtered by the path, it's not true that is the only way to achieve it. Of course, if you are using a staticQuery approach, the limitation of making it dynamic forces you to make each query separately.
First of all, you need to know the difference between staticQuery and page query to understand which fits you and the limitations of them.
If you use a page query, you can always create an approach like the following one:
import React from 'react'
import { graphql } from 'gatsby'
import Layout from '../components/layout'
class ProductPage extends React.Component {
render() {
const products = get(this, 'props.data.allDataJson.edges')
return (
<Layout>
{products.map(({ node }) => {
return (
<div key={node.name}>
<p>{node.name}</p>
<Img fluid={node.image.childImageSharp.fluid} />
</div>
)
})}
</Layout>
)
}
}
export default ProductPage
export const productsQuery = graphql`
query {
allDataJson {
edges {
node {
slug
name
image{
publicURL
childImageSharp{
fluid {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
`
In the example above, you are using a page query to retrieve all images from a JSON file. If you set the path in your filesystem, you will be able to retrieve them using the GraphQL fragments. This approach is the more dynamic you can afford when dealing with Gatsby Image and it's better to query one by one.
The idea remains the same for other filesystems, this is just an adaptable approach. If you are using a CMS like Contentful, you can download the assets and query them dynamically in the same way since the filesystem allows you to do it.
Pages queries are only allowed in page components (hence the name) so, if you want to use it in a React standalone component to make it reusable, you will need to pass via props (or reducer) to your desired component and render the Gatsby image based on the received props.

Storybook Mock GraphQL (Gatsby)

I am using Gatsby to create a blog. Gatsby can use markdown with GraphQL to "automagically" create post pages for you. I was wondering using the Gatsby example here.
In storybook UI what is the best way to "mock" out the graphql query and replace it with our markdown data. So that I can test this component in Storybook UI. For example if I have a blog template that looks something like:
import { graphql } from 'gatsby';
import React from 'react';
export default function Template({
data, // this prop will be injected by the GraphQL query below.
}) {
const { markdownRemark } = data; // data.markdownRemark holds your post data
const { frontmatter, html } = markdownRemark;
return (
<div className="blog-post-container">
<div className="blog-post">
<h1>{frontmatter.title}</h1>
<h2>{frontmatter.date}</h2>
<div
className="blog-post-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</div>
</div>
);
}
export const pageQuery = graphql`
query($slug: String!) {
markdownRemark(frontmatter: { slug: { eq: $slug } }) {
html
frontmatter {
date(formatString: "MMMM DD, YYYY")
slug
title
}
}
}
`;
Thanks in advance
You can probably modify the webpack configuration of Storybook to use the NormalModuleReplacementPlugin to mock the whole gatsby package. Then export a graphql method from your mock that you can manipulate in your stories.
Alternatively, split your component into a pure component and a component that performs the query and just use the pure component as suggested in https://www.gatsbyjs.org/docs/unit-testing/

Loading gltf in gatsby site with react-three-fiber

I've been trying to load a GLTF model into my gatsby site using react-three-fiber, but can't seem to get it to load. This seems like it should be very simple, but I'm new to Gatsby and threejs and was wondering if I could get some guidance.
My model is stored as static/models/crerar.glb, and I used gltfjsx to generate a Model component. I've tried referencing just 'models/crerar.glb' but haven't had luck either.
In index.js, I have:
import Layout from "../components/layout"
import SEO from "../components/seo"
import React, { Suspense, useRef, useState } from "react"
import { Canvas, useFrame, useLoader } from "react-three-fiber"
import Model from "../components/Crerar"
const IndexPage = () => (
<Layout>
<Canvas>
<ambientLight intensity={0.2} />
<Model />
</Canvas>
</Layout>
)
export default IndexPage
and in Crerar.js (stored in components)
/*
auto-generated by: https://github.com/react-spring/gltfjsx
*/
import * as THREE from 'three'
import React, { useRef } from 'react'
import { useLoader } from 'react-three-fiber'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
export default function Model(props) {
const group = useRef()
const { nodes, materials } = useLoader(GLTFLoader, '../static/models/crerar.glb')
return (
<group ref={group} {...props} dispose={null}>
<mesh material={nodes.mesh_0.material} geometry={nodes.mesh_0.geometry} />
</group>
)
}
the path is wrong. the json.parse error you're getting is because the loader tries to parse a HTML 404 fetch-error as a GLTF. you can make sure by opening dev tools and the networking tab. you should see it's trying to reach your file, but can't.
if the model is within your src folder you have to import it first, then use the hashed url that you get. this is my recommendation, don't mess around with public files, always import your stuff. it's safer, the compiler will complain if the file isn't present, and it's better for cache control.
otherwise, if the file is inside /public or i guess it's /static in gatsby (?) then the url has to be something like "/file.glb". sometimes it's /public/file.glb or /static/file.glb, it depends on your bundling environment (you can try fetching the file via the browsers url bar, if an url works, that's the one you should pass on to the loader).
if your file is draco compressed, then draco must also be inside public or static. see: https://codesandbox.io/s/r3f-ibl-envmap-simple-y541h
you can safely use useLoader(GLTFLoader, url), it's just a wrapper around new GLTFLoader().load(url, data => ...) + suspense. It's not experimental any longer, even though it may have that warning on Github.
gatsby copies everything from static into the public folder, so change your url to:
const { nodes, materials } = useLoader(GLTFLoader, '/models/crerar.glb')

On Gatsby, how to do a GraphQL query using a prop?

I'm passing a prop to a new component and trying to use that to do a graphql query. It looks like this.
import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import Img from "gatsby-image"
const ComponentName = props => {
const getImage = graphql`
{
image: file(relativePath: { eq: ${props.img} }) {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
`
const data = useStaticQuery(getImage)
return (
<li>
<Img
fluid={data.image.childImageSharp.fluid}
/>
</li>
)
}
export default ComponentName
But I'm getting this error BabelPluginRemoveGraphQLQueries: String interpolations are not allowed in graphql fragments. Included fragments should be referenced as...MyModule_foo.
I've tried all sorts of different tricks to get rid of the "string interpolation" error. But none of them work (it just shows a different error each time).
I'm assuming you can't do a graphql query using props? Or is there another way to do this?
From StaticQuery docs:
StaticQuery does not accept variables (hence the name “static”), but can be used in any component, including pages.
And same for the hook version useStaticQuery:
useStaticQuery does not accept variables (hence the name “static”), but can be used in any component, including pages.
You can read further in gatsby's GitHub here.

How to put Graphql queries to fetch data from Wordpress API inside react components instead of templates in GatsbyJS?

I'm working on a static website made using GatsbyJS, for which the data is hosted on Wordpress. Till now, I'm able to fetch the posts and pages data and create pages for them using templates using this plugin.
My requirement is to create a gallery or carousel section using an ACF field in any post or page in any of my existing React components. But, I'm unable to wrap my head around this as there needs to be some settings done in gatsby-node.js for this, I suppose.
I created the GraphQL query to do this and it is fetching the correct data. But now the issue is that I don't know where to put this query.
Here is the GraphQL query:
`query MyQuery {
wordpressPost(wordpress_id: {eq: 11}) {
acf {
facebook
twitter
image {
localFile {
url
}
}
slider {
image_caption
slider_image {
localFile {
url
}
}
}
}
}
}`
Please let me understand this better as I'm kind of stuck in this.
To create query in component simply use staic query. The example has been copy pasted from gatsby documentation:
import React from "react"
import { StaticQuery, graphql } from "gatsby"
export default () => (
<StaticQuery
query={graphql`
query HeadingQuery {
site {
siteMetadata {
title
}
}
}
`}
render={data => (
<header>
<h1>{data.site.siteMetadata.title}</h1>
</header>
)}
/>
)

Resources