Showing a list of images with Gatsby Image and Graphql - graphql

I have been working with Gatsby and GraphQL on a project for a bit now. This is my first time using this platform so still figuring out the right way to go about it.
Right now, I have an index.js which loads a list of users from a GraphQL query such as
allUserList {
users {
name
imageUrl
}
}
}
imageUrl is expected to send the correct file path i.e images/image.png
This is sent directly to a react component which is used to display the user info
const Users = {data} => {
return (
data.users.map(user => {
return (
<User name={user.name} imageUrl={user.imageUrl}/>
)
}
)
}
What I am trying to figure out now is, imageUrl contains the relative url to the image in the codebase. I would be required to run a image query on this url to get the image object which I can then pass to gatsby image. Something like this
{
image: file(relativePath: { eq: 'image.png' }) {
childImageSharp {
fixed(width: 160, height: 160) {
...GatsbyImageSharpFixed
}
}
}
}
However as I understand it isnt possible to parameterize the image url here. What is the best way to go about this? Is this even the correct approach to show a list with images in this manner?

However as I understand it isnt possible to parameterize the image url here.
Correct. Gatsby runs GraphQL queries ONCE at build time and then never again until you gatsby develop or gatsby build again.
Two approaches:
First approach: You name your images after user.name. You can then query all your images and filter for each user by file name using the graphQL originalName attribute:
const UserSupplier = () => {
const { allFile } = useStaticQuery(graphql`
query {
allFile(filter: {
extension: {regex: "/(jpg)|(jpeg)|(png)/"},
sourceInstanceName: {eq: "user"}})
{
edges {
node {
childImageSharp {
fluid(maxWidth: 150, quality: 100) {
originalName
...GatsbyImageSharpFluid
}
}
}
}
}
}`);
const user = users.filter(el => el.userId === userId)[0];
const { fluid } = user .node.childImageSharp;
return (
<UserComponent>
<GatsbyImage fluid={fluid} alt={fluid.originalName} />
</UserComponent>
);
};
Second approach: Query for your childImageSharp image object in your allUserList query. You can pass the image as props to the component that renders the image. That would make your query result query quite data intensive depending on how many users there are.
allUserList {
users {
name
imageUrl
childImageSharp { // the query path is probably wrong since I don't know your project
fixed(width: 160, height: 160) {
...GatsbyImageSharpFixed
}
}
}
}
}

Related

Gatsbyjs + Directus how to get image from content?

I connected gatsby and directus. everything is ok, but...
I have html text field with name "content" in directus, it has images inside. how do i get them in gatsby?
If you want to display your image inside the raw HTML (content) you will need to use dangerouslySetInnerHTML or use a markdown parser. The imageFile node can be isolated and print alone.
You just need to use a page query to fetch your data like:
import * as React from 'react'
import { graphql } from 'gatsby'
import Markdown from 'markdown-to-jsx';
const HomePage = ({data}) => {
console.log("Your data is", data);
return (
<div>
Your image is: <img src={data.article.image.imageFile.publicURL} alt="" />
Your content is:
<Markdown>{data.article.content}</Markdown>
</div>
)
}
export const query = graphql`
query directus{
article {
id
title
description
content
image {
imageFile {
publicURL
}
}
}
}
`
export default HomePage
Note: I've based my query in your GraphQL schema, tweak it to adapt it to yours.
Your queried data is stored inside props.data (destructured as data in this case), then you just need to get the needed node in each case. For your image, you need to keep nesting the object until your reach publicURL.
Regarding the rest of the content, I've used markdown-to-jsx library which is quite straightforward to use, but you can omit it and use directly dangerouslySetInnerHTML or another library.
I solved the problem as follows:
I created a multiplefiles field in Directus in the Article named "imagelist" there indicated the same images that I use in the article.
Now gatsby gives me an array of images in GraphQL.
Wrote a CustomImage component for the image, where I check the id.
Used the overrides option to replace the img tag in the Markdown component.
So, my code looks that:
layout props:
interface ArticleImage {
directus_files_id: {
id: string;
imageFile: IGatsbyImageData;
}
}
interface BlogLayoutProps {
title: string;
annotation: string;
imageData: IGatsbyImageData;
allImages: Array<ArticleImage>;
content: string;
}
custom image component:
const CustomImage = (props) => {
const { alt, width, height, src } = props;
const image = allImages.find(i => src.indexOf(i.directus_files_id.id) !== -1);
if (image) {
const imgData = getImage(image.directus_files_id.imageFile);
imgData.width = +width;
imgData.height = +height;
return <GatsbyImage image={imgData} alt={alt}/>;
}
return <img src={src} width={width} height={height} alt={alt} />
};
in layout component:
<StyledContent>
<Markdown
options={{
overrides: {
img: {
component: CustomImage,
}
}
}}
>
{content}
</Markdown>
</StyledContent>
query in page:
query MyQuery {
directus {
article_by_id(id: "26434049-7bb4-46d3-8ad1-c04973b9cf71") {
id
category {
id
title
}
title
description
content
image {
id
width
height
imageFile {
childImageSharp {
gatsbyImageData
}
}
}
imagelist {
directus_files_id {
id
imageFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
}
I hope someone will help my solution or push him to new thoughts!

Using Gatsby Background with Multiple Images

I am trying to use gatsby-background-image with multiple images but I can't get it to work.
I have the following
const {backgroundImages} = useStaticQuery(graphql`
query {
backgroundImages: allFile(
filter: {extension: {regex: "/(png)/"}, relativeDirectory: {eq: "slider"}}
) {
edges {
node {
base
childImageSharp {
gatsbyImageData(width: 10, quality: 10, webpOptions: {quality: 70})
}
}
}
}
}
`)
const image = getImage(backgroundImages.edges[0].node.childImageSharp)
const bg = convertToBgImage(image)
This is how I am using the BackgroundImage component
<BackgroundImage Tag="section" {...bg} preserveStackingContext className={styles.bgImg}>
{props.children}
</BackgroundImage>
I've tried accessing the gatsbyImageData property of the object but that didn't work either.
You are only getting the first image (node) in:
const image = getImage(backgroundImages.edges[0].node.childImageSharp)
If you use a loop with:
const images = [];
backgroundImages.edges.map(backgroundImage => images.push(backgroundImage.node.childImageSharp)
Once you've stored your nodes, you just need to print it like:
images.map(image => {
let bg= convertToBgImage(image);
return <BackgroundImage Tag="section" {...bg} preserveStackingContext className={styles.bgImg}>
{props.children}
</BackgroundImage>
})

How to use data from GraphQL in a React component in GatsbyJS

I'm trying to query for a specific category title and returning it inside a div. It gives me the following error message:
TypeError: can't access property "node", data.edges is undefined
This is my file:
import React from "react"
import { graphql, useStaticQuery } from "gatsby"
import styled from "styled-components"
import { H3, BodyMain } from "../styles/TextStyles"
export default function CategorySection() {
const data = useStaticQuery(graphql`
query categoryQuery {
allGraphCmsCategory(filter: { title: { eq: "CSS" } }) {
edges {
node {
title
slug
description
}
}
}
}
`)
return (
<Wrapper>
<ContentWrapper>
<TextWrapper>
<Title>Browse by Categories</Title>
<Description>
Use the category tags to narrow down what you are looking for.
</Description>
</TextWrapper>
<CategoryWrapper>
<Categories>{data.edges.node.title}</Categories>
</CategoryWrapper>
</ContentWrapper>
</Wrapper>
)
}
const Wrapper = styled.div``
const ContentWrapper = styled.div``
const TextWrapper = styled.div``
const Title = styled(H3)``
const Description = styled(BodyMain)``
const CategoryWrapper = styled.div``
const Categories = styled.div``
I believe my query is right, as I'm able to see results on http://localhost:8000/___graphql
When I have tested it and see it work, I would like to map through all categories and create separate pages for each.
Can you guide me in the right direction?
Your query looks good, however, you need to access the nested object as your GraphQL shows, in your case, this should work:
export default function CategorySection() {
const data = useStaticQuery(graphql`
query categoryQuery {
allGraphCmsCategory(filter: { title: { eq: "CSS" } }) {
edges {
node {
title
slug
description
}
}
}
}
`)
console.log("your data is", data.allGraphCmsCategory.edges) // use to access to the nested data data.allGraphCmsCategory.edges[0].node.title
return (
<Wrapper>
<ContentWrapper>
<TextWrapper>
<Title>Browse by Categories</Title>
<Description>
Use the category tags to narrow down what you are looking for.
</Description>
</TextWrapper>
<CategoryWrapper>
<Categories>{data.edges.node.title}</Categories>
</CategoryWrapper>
</ContentWrapper>
</Wrapper>
)
}
Note that, inside data, you first need to access allGraphCmsCategory and keep following the object tree. I've assumed (because of the all keyword in allGraphCmsCategory) that the result will have multiple edges nodes (array), that's why the edges[0].
Alternatively, you can use the StaticVersion component:
export default function CategorySection() {
return (
<StaticQuery
query={graphql`
query categoryQuery {
allGraphCmsCategory(filter: { title: { eq: "CSS" } }) {
edges {
node {
title
slug
description
}
}
}
}
`}
render={data => {
console.log(data);
return (
<Wrapper>
<ContentWrapper>
<TextWrapper>
<Title>Browse by Categories</Title>
<Description>
Use the category tags to narrow down what you are looking for.
</Description>
</TextWrapper>
<CategoryWrapper>
<Categories>{data.allGraphCmsCategory.edges[0].node.title}</Categories>
</CategoryWrapper>
</ContentWrapper>
</Wrapper>
)
}}
/>
)
}
To make it dynamic:
{data.allGraphCmsCategory.edges.map(item=>{
return <Categories>{item.title}</Categories>
})}

Graphql Filter out specific images to not show up?

My current image component maps through all the images in my folder and displays them, but I want to filter out two of my images so I can apply them to another page instead of displaying on the current page with all the other images in my folder.
How would I write my filter to remove the 2 images from showing up?
I have one image called background.jpg and background-2.jpg that I want to filter out
const data = useStaticQuery(graphql`
query {
allFile(filter: { extension: { regex: "/(jpg)|(png)|(jpeg)/" },
// So this line filters out background.jpg, but I can't seem to
filter out both background.jpg and background-2.jpg?
base: {ne: "background.jpg"}
}) {
edges {
node {
base
childImageSharp {
fluid(maxHeight: 600, maxWidth: 600) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`)
You can filter the node name with nin (even another regex) or ne rule.
const data = useStaticQuery(graphql`
query {
allFile(filter: {
extension: { regex: "/(jpg)|(png)|(jpeg)/" } }
name: {ne: "background"})
{
edges {
node {
base
childImageSharp {
fluid(maxHeight: 600, maxWidth: 600) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`)
The query above should do the trick. If not, you can always use nin rule to create an array of exceptions or use another regex to exclude the background name.
More information: https://www.gatsbyjs.com/docs/graphql-reference/#filter

How to get Gatsby Images based on results of PageQuery?

I'd like to do something like the following so I can get Gatsby Images dynamically:
const image = 'gastby-astronaut.png';
export const imageQuery = graphql`
{ allImageSharp (
filter: {
fluid: {
originalName: {
regex: "/${image}/"
}
}
}
){
edges {
node {
fluid {
originalName
}
}
}
}
}
`;
However, I can't figure out how to connect this query to an initial query that would get the 'gatsby-astronaut.png', or perform this query from a subcomponent with a . I get this error when I try this:
Error: BabelPluginRemoveGraphQL: String interpolations are not allowed
in graphql fragments. Included fragments should be referenced as
`...MyModule_foo`.
Any suggestions on the proper way to return Gatsby Images dynamically?
Ah, yeah Gatsby extracts GraphQL queries from your pages through static analysis: they load the file as text, parse it, and extract the query, all before the actual file gets executed. This means that your typical tagged-template literal functionality isn't there.
The only way to filter is through context provided when createPage is called from gatsby-node.js. I.e. if you do this:
exports.createPages = ({ graphql, actions }) =>
graphql(`some query here`).then(result => {
actions.createPage({
path: "/output-path/",
component: path.resolve(`./src/templates/your_template.jsx`),
context: { image: result.data.yourImage },
})
})
Then you can do this in your page query:
query SomePage($image: String!) {
allImageSharp (
filter: {
fluid: {
originalName: {
regex: $image
}
}
}
){
edges {
node {
fluid {
originalName
}
}
}
}
}
Here's a solution I came up with... pretty janky, but it works:
import PropTypes from 'prop-types';
import React from 'react';
import Img from 'gatsby-image';
import { useStaticQuery, graphql } from 'gatsby';
const Image = ({ imageYouWant }) => {
const data = useStaticQuery(
graphql`
query allTheImagesQuery{
allImageSharp {
edges {
node {
fluid(maxWidth:1000) {
...GatsbyImageSharpFluid
originalName
}
}
}
}
}`,
);
const TheImageYouWant = data.allImageSharp.edges
.filter(edge => edge.node.fluid.originalName === imageYouWant)
.map(myImage => <Img fluid={myImage.node.fluid} />);
return (
<>
{ TheImageYouWant }
</>
);
};
Image.propTypes = {
imageYouWant: PropTypes.string,
};
Image.defaultProps = {
imageYouWant: '',
};
export default Image;

Resources