I am trying for the first time GatsbyJs and I see the feature with ImageSharp for optimizing images.
Now, I created a component and it works fine, but I would like to optimize the code to be able to add more images.
The code is organized in this manner:
I have the component images.js, import this component in the index page. In the component at this moment have 2 images (1.jpg, 2.jpg)
I would like to create two returns of a single component image, see my code of images.js component below:
import React from "react"
import { useStaticQuery, graphql } from "gatsby"
import Image from "gatsby-image"
const ImagesHome = () => {
const data = useStaticQuery(graphql`
query BioQuery {
imgOne: file(absolutePath: { regex: "/1.jpg/" }) {
childImageSharp {
fixed(width: 710) {
...GatsbyImageSharpFixed
}
}
}
imgTwo: file(absolutePath: { regex: "/2.jpg/" }) {
childImageSharp {
fixed(width: 710) {
...GatsbyImageSharpFixed
}
}
}
}
`)
return (
<div>
<Image
fixed={data.imgOne.childImageSharp.fixed}
/>
<div id="secondImageHome">
<Image
fixed={data.imgTwo.childImageSharp.fixed}
/>
</div>
</div>
)
}
export default ImagesHome
I would use the export component image in single code like:
<Img fluid={props.data.imageOne.childImageSharp.fluid} />
<Img fluid={props.data.imageTwo.childImageSharp.fluid} />
Will it be possible with my code? I tried some tutorials on YouTube, but it did not work.
Related
The detail of the problem is that I'm not getting the data we expected within our code. We have set up the following to see within our project folder.
Within webpack.config.js we set up resolve.alias in the webpack configuration, so we can include it in the resolve object of the webpack config file.
This is the file:
module.exports = {
// other webpack config options
resolve: {
alias: {
// Create an alias for the queries folder
queries: path.resolve(__dirname, 'src/queries'),
},
},
};
This is what are path name will look like:
import { homeQuery } from "queries/navigation/homeLinks";
Created a webpack.config.js.
Console.log are data of homeData and HomeData.homelink.
Tested the query within GraphQL Playground in Hygraph.
From there I checked the console in the browser.
I have found out we are not getting the query in our body that we want but we have tested the query within GraphQL Playground. We have found out that we correctly getting the correct query that we want to get into our data within console.log.
This is a query for the homepage:
import { gql } from "#apollo/client";
export const homeQuery = gql`
query homeLinks {
pages(
where: {id: "clbzl7ovpe64d0ak5qh7o2p8f", slug: "Home"}
stage: PUBLISHED
locales: en
) {
id
slug
title
}
}`
;
This where we console are data for the homepage:
console.log(homeData && homeData?.homeLink?.pages);
console.log(homeData)
import React from 'react';
import '../assets/style/navigation.css';
import { useQuery } from '#apollo/client';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import { homeQuery } from "../queries/navigation/homeLinks";
import { logoassetsQuery } from "../queries/navigation/logoLinks";
export default function App() {
const { loading: logoAssetsLoading, error: logoassetsData } = useQuery(logoassetsQuery);
//This is query for the pages.
const { loading: homeLoading, error: homeError, data: homeData } = useQuery(homeQuery);
//This is for front-end error message.
if (logoAssetsLoading) return <p>Loading Logo Assets...</p>;
if (homeLoading) return <p>Loading... this page links.</p>
if (homeError) return <p>Error :( this page don't work.</p>;
// Debugging step 1: Check the value of data.navigation
console.log(homeData && homeData.homeLinks)
console.log(homeData)
// Debugging step 2: Check the value of data
//console.log(data);
return (
<div className='navigation'>
<div className='full_width_container'>
<div className='wrapper'>
<Router>
<React.Fragment>
<nav>
<div className='nav_groups logo'>
{homeData && homeData?.homeLinks?.pages && homeData?.homeLinks?.page(link => (
<li key={link?.id}>
<Link to={`/${link?.slug}`}>
<img src={logoassetsData?.logoAssets} alt='main logo'/>
</Link>
</li>
))}
</div>
</nav>
</React.Fragment>
</Router>
</div>
</div>
</div>
);
}
I've simple react component, however I need to have a story for this one as well. Could you please help in writing it?
I have the sample one and I am not sure how to pass the component inside it.
So far I have those:
component:
import { string, urlPropType } from "prop-types"
import * as Styled from "./Image.styled"
import image from "./components/image/cutedog.jpg"
const Image = ({ src, alt }) => (
<Styled.Image>
<img src={image} alt={dog} />
</Styled.Image>
)
Image.propTypes = {
src: urlPropType.isRequired,
alt: string.isRequired,
}
export default Image
Story:
import { Meta, Canvas, Story, ArgsTable } from "#storybook/addon-docs"
import Image from "../Image"
<Meta title="Components/Image" component={Image} />
# Image
<Canvas>
<Story
name="Overview"
args={{
name: "arrowDown",
size: "medium",
}}
>
{Template.bind()}
</Story>
</Canvas>
<ArgsTable />
export const Template = (args) => <Image {...args} />
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've been searching for a proper guidance for integrating lightgallery.js library into my application, but after several hours I did not find any solutions. Since I'm using React, I don't want to mix it with JQuery.
I've stumbled across many similar questions like this one, but since all of them are using JQuery, I can't use their solutions.
Also, I've found react-lightgallery package (React wrapper for lightgallery.js), but it does not include video support yet.
In the lightgallery.js documentation, there is the installation guidance. After completing all of the steps, importing lightgallery.js and trying to print it (as suggested here by the library owner), empty object is being shown.
What would be the best solution for this? Are there any good alternatives?
Thanks!
I have handled it this way. May be it's not complete and the best practice, but it gives you a general view to how to handle it
import React, { PureComponent } from "react";
import Gallery from "lightgallery.js";
import "lightgallery.js/dist/css/lightgallery.min.css";
class _Gallery extends PureComponent {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
let self = this;
this.gallery = document.getElementById("lightgallery");
lightGallery(this.gallery, {
dynamic: true,
dynamicEl: [
{
src:
"1.jpg",
thumb: "1.jpg",
subHtml:
"<h4>Fading Light</h4><p>Classic view</p>"
},
{
src:
"2.jpg",
thumb: "2.jpg",
subHtml:
"<h4>Bowness Bay</h4><p>A beautiful Sunrise</p>"
},
{
src:
"3.jpg",
thumb: "3.jpg",
subHtml: "<h4>Coniston Calmness</h4><p>Beautiful morning</p>"
}
]
});
this.gallery.addEventListener("onCloseAfter", function(event) {
window.lgData[self.gallery.getAttribute("lg-uid")].destroy(true);
self.props.onCloseGallery();
});
}
render() {
return <div id="lightgallery" />;
}
}
export default _Gallery;
Here is a working example with cloudinary at Cloudinary LightGallery
The code for the Cloundinary LightGallery React Component using Styled Components and styled css grid is below.
The Code for the upload component is in my GitHub Repo at.
UploadWidget
/* eslint-disable indent */
import React, { Component, Fragment } from 'react'
import { LightgalleryProvider, LightgalleryItem } from 'react-lightgallery'
import axios from 'axios'
import styled from 'styled-components'
import { CloudinaryContext, Transformation, Image } from 'cloudinary-react'
import { Grid, Cell } from 'styled-css-grid'
import { media } from '../../utils/mediaQuery'
import 'lightgallery.js/dist/css/lightgallery.css'
import 'lg-autoplay.js'
const SectionTitle = styled.h3`
font-size: 1em;
margin: 0.67em 0;
${media.xs`
font-size: .85em;
`}
`
class Gallery extends Component {
constructor (props) {
super(props)
this.link = React.createRef()
this.state = {
gallery: [],
isOpen: false,
link: this.href,
}
}
componentDidMount () {
// Request for images tagged cats
axios.get('https://res.cloudinary.com/mansbooks/image/list/v1557911334/cats.json')
.then(res => {
console.log(res.data.resources)
this.setState({ gallery: res.data.resources })
})
}
onLink (event) {
this.setState({ link: this.href =
`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg` })
}
uploadWidget () {
let _this = this
cloudinary.openUploadWidget({ cloud_name: 'mansbooks', upload_preset: 'photos-
preset', tags: ['cats'], sources: ['local', 'url', 'camera', 'image_search',
'facebook', 'dropbox', 'instagram'], dropboxAppKey: 'Your API Key', googleApiKey:
'Your API Key' },
function (error, result) {
// Update gallery state with newly uploaded image
_this.setState({ gallery: _this.state.gallery.concat(result) })
})
}
render () {
return (
<div>
<Fragment>
<SectionTitle>Gallery by Cloudinary</SectionTitle>
<div>
<CloudinaryContext cloudName='mansbooks'>
<Grid columns='repeat(auto-fit,minmax(260px,1fr))' id='hash'>
<LightgalleryProvider>
{
this.state.gallery.map(data => {
return (
<Cell key={data.public_id}>
<LightgalleryItem group='group1' src={`https://res.cloudinary.com/mansbooks/image/upload/${data.public_id}.jpg`} data-sub-html={'data.public_id'}>
<Image publicId={data.public_id} onClick={() => this.setState({ isOpen: true })}>
<Transformation
crop='scale'
width='250'
height='170'
radius='6'
dpr='auto'
fetchFormat='auto'
responsive_placeholder='blank'
/>
</Image>
</LightgalleryItem>
</Cell>
)
})
}
</LightgalleryProvider>
</Grid>
</CloudinaryContext>
</div>
</Fragment>
</div>
)
}
}
export default Gallery
Im building a blog with Gatsby and I'm trying to display a hero image on each post page with the image path that is defined in each posts front matter. But, I'm getting this error from my hero component:
TypeError: Cannot read property 'image' of undefined
Here is my code:
Post front matter
---
path: /blog-post1
title: Blog Post 1
image: ../../images/blog-post-1.jpg
description: Blog post description
---
Hero.js
import React from 'react'
import Img from "gatsby-image";
const Hero = props => (
<section className="hero is-large">
<Img
fluid={props.frontmatter.image.childImageSharp.resize}
/>
<div className="hero-body">
</div>
</section>
);
export default Hero
Post.js
import React from 'react';
import { graphql } from 'gatsby';
import Layout from '../components/layout';
import Hero from '../components/hero';
const PostTemplate = ({ data }) => {
const { markdownRemark } = data;
const { frontmatter, html } = markdownRemark;
return (
<Layout>
<Hero headerImage={frontmatter.image} />
<section class="section">
<div className="container is-medium">
<div dangerouslySetInnerHTML={{__html: html}} />
</div>
</section>
</Layout>
)
}
export default PostTemplate;
export const pageQuery = graphql`
query($path: String!) {
markdownRemark(frontmatter: { path: { eq: $path } }) {
html
frontmatter {
date
path
title
description
image {
childImageSharp {
resize(width: 1500, height: 1500) {
src
}
fluid(maxWidth: 786) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
`;
Any ideas on what's happening? Thanks in advance.
I am assuming you have data in frontmatter variable all the time. If you look at how you are calling Hero Component, where you are passing data as headerImage.
<Hero headerImage={frontmatter.image} />
If you look at Hero component you are reading it as frontmatter, You can do following changes and check.
You can add the condition to Img component to avoid the errors because of missing data.
{
props.headerImage && props.headerImage.image && props.headerImage.image.childImageSharp
<Img
fluid={props.headerImage.image.childImageSharp.resize}
/>
}
I figured out was the issue.
In my hero.js, I was calling the image using {props.frontmatter.image.childImageSharp.resize}
But in my post.js I was passing the data into the hero component like this <Hero headerImage={frontmatter.image} />
So, it was trying to find frontmatter.image.image, which doesn't exist. All I had to do was remove the .image like this <Img fluid={props.headerImage.childImageSharp.resize} />