menu

Gatsby.js

Fast in every way that matters. Gatsby is a free and open source framework based on React that helps developers build blazing fast websites and apps.

Channels
# All channels
view-forward
# General
view-forward
# I Made This
view-forward
# Meta
view-forward
# Themes
view-forward
Team

Using Variables in a StaticQuery

December 13, 2018 at 10:05pm

Using Variables in a StaticQuery

December 13, 2018 at 10:05pm
Hey Folks - I'm using gatsby-image in my latest project and I'm finding it a bit cumbersome to have to write a whole query just to display an image. I would love to just have a component that I pass a src to and it does it all under the hood.
So - I'm writing a component with StaticQuery that looks like this:
import React from 'react';
import Img from 'gatsby-image';
import { StaticQuery, graphql } from 'gatsby';
function renderImage({ file }) {
return <Img fluid={file.childImageSharp.fluid} />
}
const MyImg = function (props) {
return <StaticQuery
query={graphql`
query {
file(relativePath: { eq: "wes-and-scott.jpg" }) {
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
}
`}
render={renderImage}
/>
}
export default MyImg;
Now that works amazingly well, BUT I can't make the filename dynamic.
I read in the docs that Static Query can't take variables - only pages. But I don't want my images to be associated with a page - I want to use this component anywhere I want - like a regular img tag.
I tried string interpolation and that doesn't work.
Any way to get around this? Am I thinking about this in the wrong way?

December 13, 2018 at 10:16pm
Hey Wes — definitely agree that allowing components w/ StaticQuerys take arguments would be amazing. This isn't something we've yet tackled but it should be doable with sufficient babel/code analysis magic :-)
  • reply
  • like
One constraint would be that variables passed to these components would still need to be "static" meaning they're numbers/strings.
like-fill
5
  • reply
  • like
I have not used gatsby, but I think you can hack the graphql. This is something untested though:
import React from 'react';
import Img from 'gatsby-image';
import { StaticQuery, graphql } from 'gatsby';
function renderImage({ file }) {
return <Img fluid={file.childImageSharp.fluid} />
}
const MyImg = function (props) {
function hackTheQuery(src) {
return `
query {
file(relativePath: { eq: "${src}" }) {
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
}
`
}
return <StaticQuery
query={graphql([hackTheQuery(props.src)])}
render={renderImage}
/>
}
export default MyImg;
  • reply
  • like
Yeah, that doesn't work unfortunately (that's the "string interpolation" Wes tried) as queries have to be evaluated at "build-time". So stuff like this can be done with sufficient effort but since code isn't actually running, we don't get stuff like this for free
  • reply
  • like
and this is what I use in order to reuse my Static Queries and hack them into being somewhat dynamic:
import React from 'react';
import PropTypes from 'prop-types';
import { graphql, StaticQuery } from 'gatsby';
function TestimonialQuery({ children, identifier }) {
return (
<StaticQuery
query={graphql`
query {
allContentfulTestimonial {
edges {
node {
identifier
image {
title
fluid(maxWidth: 437) {
...GatsbyContentfulFluid_withWebp
}
}
}
}
}
}
`}
render={({ allContentfulTestimonial }) =>
children(
allContentfulTestimonial.edges.find(
({ node }) => node.identifier === identifier,
),
)
}
/>
);
}
TestimonialQuery.propTypes = {
children: PropTypes.func.isRequired,
identifier: PropTypes.string.isRequired,
};
export default React.memo(TestimonialQuery);
Then I just use it as a render prop:
<TestimonialQuery>
{node => <div>node.identifier</div>}
</TestimonialQuery>
The same could be done by passing file names to your query . It's not the most elegant, but it seems to work for well for me and reduces boilerplate.
Edited
  • reply
  • like
I had looked into this at one point, this issue discussed some attempts at working some magic with Babel but never seemed to reach a conclusion: https://github.com/gatsbyjs/gatsby/issues/2293#issuecomment-418542566
Of existing Gatsby starters and showcase sites where lots of <Img> components are used, the common pattern seems to be with the image data queried in a big list of edges like in this starter (https://github.com/greglobinski/gatsby-starter-hero-blog/blob/3db65f5badd52c5b50924180c0b0f7d68870bb07/src/components/Blog/Item.js#L32) or this one (https://github.com/LekoArts/gatsby-starter-portfolio-emma/blob/1e91b715523b5506f3c29308d8fb7bf0d38ce4dc/src/components/ProjectListing.jsx#L84).
I guess that pattern doesn't match your use case with StaticQuery exactly though.
Edited
  • reply
  • like
Be careful with that approach, it will download the metadata (including base64 encoded image!) of EVERY SINGLE image you have and then filter it on the client. Depending on how many images you have, that can chew up a lot of bandwidth.
like-fill
1
  • reply
  • like
A few people on twitter were recommending this approach:
Say I have ~200 images. Would this be a memory issue? I would assume it would be but so many people have said they go this way
  • reply
  • like
I've settled on this for now - unsure if it will cause perf issues down the road - we will see! Thanks for the help everyone!
  • reply
  • like
import React from 'react';
import Img from 'gatsby-image';
import { StaticQuery, graphql } from 'gatsby';
function renderImage(file) {
console.log(file);
return <Img fluid={file.node.childImageSharp.fluid} />
}
const MyImg = function (props) {
return <StaticQuery
query={graphql`
query {
images: allFile(filter:{ extension: { regex: "/jpeg|jpg|png|gif/"}}) {
edges {
node {
extension
relativePath
childImageSharp {
fluid(maxWidth: 1000) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`}
render={({ images }) => renderImage(images.edges.find(image => image.node.relativePath === props.src))}
/>
}
export default MyImg;
like-fill
2
  • reply
  • like

December 14, 2018 at 1:51pm
I love Gatsby, and all I want for Christmas is for this to be a reality: https://github.com/gatsbyjs/gatsby/issues/9843
like-fill
2
  • reply
  • like
Hey Wes, what I've done, without seeing too much of a performance hit, seems to be exactly what you're suggesting: namely, create a component that queries all image nodes in a specific location and filter the results, finding match and returning it (or a default placeholder, if no match is found).
  • reply
  • like
In order to speed things up, I've broken the images into groups (maybe I have Bio images, Cover images, and Dog images... I'd have <DogImage /> <BioImage /> and <CoverImage />).
  • reply
  • like
And they point to images/dogs, images/bios, images/covers
  • reply
  • like
I'd like to look into the performance implications of this, too. I'm wondering if, since to my understanding the first render and the StaticQuery itself happens on build, and not browser-side, the hit would even be noticeable.
Edited
  • reply
  • like
just as a follow up, I set up the following netlify to test this: https://dazzling-bohr-1d9d3d.netlify.com/ using 200 random unsplash images.
  • reply
  • like
The homepage uses 200 components, each with its own StaticQuery and /page-2/ uses a single component with a single StaticQuery, being filtered.
  • reply
  • like
Not sure how much to trust the Chrome profiler, but here's the first page's snapshot
  • reply
  • like
  • reply
  • like
And the second
  • reply
  • like
  • reply
  • like
Thanks for doing that. So from your results, what do they mean?
  • reply
  • like
No problem! I'm not an expert here, but I would say that it means that it means that using a reusable component is worth it! The browser doesn't spend any extra time running the component logic (filtering the 200 images 200 times).
  • reply
  • like
first off, thanks for putting in that work. But to be honest, it kind of misses my point - which is reusing the same image component for multiple pages. Where the individual StaticQuerys would only load exactly the image data that is required for that page, while the reusable component in question would load all images even if they're not used on that page.
  • reply
  • like
Show more messages