I'm trying to use the Gatsby-plugin "gatsby-remark-images" to create blurry images on load which eventually renders the proper sized images when load is complete, but I might be doing this erroneously.
My index page has a Hero component, which is passed down the src of the image as such:
import Image from "../posts/img/Image.jpg";
...
<Hero id="indexPage" src={Image} />
My index page query fetches the 6 latest blogposts as such:
export const pageQuery = graphql`
query IndexQuery {
allMarkdownRemark(
limit: 6
sort: { fields: [frontmatter___date], order: DESC }
filter: { frontmatter: { category: { eq: "blog"} } }
) {
edges {
node {
fields {
slug
}
excerpt(pruneLength: 100)
timeToRead
frontmatter {
title
description
tags
date
category
thumbnail {
childImageSharp {
responsiveSizes(quality: 50, cropFocus: CENTER, toFormat: JPG) {
src
srcSet
sizes
base64
}
}
}
}
}
}
}
}
`;
Questions:
Do I need to add the Image to the query, in order to use the base64 method? In that case, how do I do that?
If I want to add the blur up technique to the images fetched from the query, what do I have to do? Do I need to add a state-change between loaded and loading, and change between base64 and src accordingly?
gatsby-remark-image is only for processing images linked to from within markdown files. See its docs https://www.gatsbyjs.org/packages/gatsby-remark-images/
For general image handling, see gatsby-image which has special GraphQL fragments & React component which makes it easy to add responsive, lazy images https://www.gatsbyjs.org/packages/gatsby-image/
Related
I have created a Gatsby blog using Flexiblocks. The issue is i want to create a sitemap specifically for Google News which will be different from main sitemap of the blog. For this sitemap i need to filter articles which were published within last two days.
I am using this plugin
allArticle(sort: {order: DESC, fields: date}, filter: {date: {WHAT_SHOULD_I_DO_HERE}}) {
edges {
node {
date
excerpt
title
link
slug
}
}
}
Any help will be appreciated, thanks.
UPDATE
{date: {gte: "2021-08-27T13:11:30.443Z"}} will filter the articles, but how can i get this date string dynamically?
Gatsby relies on momentjs under the hood when dealing with dates in GraphQL scope. You just need to add the variable returned from a function in the scope of the query like:
const getTwoDaysAgo=()=> moment().subtract(2, "days");
In your gatsby-config.js you just can call this function and:
To add a variable you can follow thee answers in: Variables in graphQL queries
Basically, following this syntax:
grapqhl(
`query getImages($fileName: String) {
landscape: file(relativePath: {eq: $fileName}) {
childImageSharp {
fluid(maxWidth: 1000) {
base64
tracedSVG
aspectRatio
src
srcSet
srcWebp
srcSetWebp
sizes
originalImg
originalName
}
}
}
}
`,
{fileName: "knight.jpg"}
)
You can add custom variables. In this case, note that the query is wrapped inside graphql() function. Applied to your case, you can try:
plugins: [
{
resolve: `gatsby-plugin-advanced-sitemap`,
options: {
query: graphql(
`
query allArticle(
sort: {
order: DESC, fields: date
},
filter: {date: {twoDaysAgo}}) {
edges {
node {
date
excerpt
title
link
slug
}
}
}`, {twoDaysAgo: getTwoDaysAgo()}
)
}
}
]
Note: be careful copy/pasting because there's a lot of brackets that I may miss.
I'm not sure if the plugin will support that notation but it should.
{date: {gte: "2021-08-27T13:11:30.443Z"}}
Given the edited question, adapt the flow and the filter to the gte parameter.
A few days ago, I bought a Gatsby blog theme and tried to modify it. The blog site uses Images(PNG, JPEG), not animated GIFs. So I tried to use GIF images for all blog posts but it affected site performance.
Also, I notice that Gatsby Image doesn't provide a GIF format. How can I use GIF on my blog with high performance?
You can convert GIFs into MP4 videos with H.264 encoding using ffmpeg. Then use <video src="..." /> in place of your img tag. To make this really easy, I have a React component that I use for this that includes automatic playback when the video is visible:
import React, { useEffect } from "react"
import PropTypes from "prop-types"
import { useInView } from "react-intersection-observer"
const GifVideo = ({ threshold = 0.15, ...playerProps }) => {
const [ref, inView] = useInView({ threshold })
useEffect(() => {
if (inView) {
ref.current?.play()
} else {
ref.current?.pause()
}
}, [ref, inView])
return <video ref={ref} autoPlay playsInline muted loop {...playerProps} />
}
export default GifVideo
GifVideo.propTypes = {
src: PropTypes.string,
threshold: PropTypes.number,
className: PropTypes.string,
}
Then to you use it, it's this easy:
<GifVideo src="/your/video.mp4" width={400} className="some-class" />
For what it's worth, I don't recommend using the sharp-backed GraphQL image transformers in Gatsby (gatsby-transformer-sharp). It's exceedingly slow, couples the presentation to the query, and doesn't provide any way to handle art direction.
I use gatsby-remark-interactive-gifs plugin to show gifs on my gatsby blog.
Install gatsby-remark-interactive-gifs
npm install --save gatsby-remark-interactive-gifs
yarn add gatsby-remark-interactive-gifs
Add this config to gatsby-config.js:
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [
{
resolve: `gatsby-remark-interactive-gifs`,
options: {
root: `${__dirname}`,
src: `${__dirname}/content/gif`,
dest: `${__dirname}/public/static/gifs`,
play: `${__dirname}/src/img/play.gif`,
placeholder: `${__dirname}/src/img/play.gif`,
loading: `${__dirname}/src/img/play.gif`,
relativePath: `/static/gifs`,
},
},
],
},
},
From plugin document:
root - The root of your project.
src - Where all the gifs you want processed are stored. Absolute path.
dest - A path in public where your gifs are stored. Absolute path.
play - An image to indicate that the gif can be interacted with. Absolute path.
placeholder - An image to show when the gif is missing in action. Absolute path.
loading - An image which shows when the gif is downloading. Absolute path.
relativePath - The relative path served from public/.
! Make sure you are adding this above the prismjs config.
Sample code in MD file to show gifs on your gatsby blog:
<img src="/static/gifs/fileName.gif">
I'm trying to utilize the Lightbox component from Gatsby Starter Lightbox to display a few image galleries on different pages.
As the starter component is setup by default, it only supports one directory of images, as set in gatsby-config.js
`gatsby-plugin-styled-components`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `cars`,
path: `${__dirname}/src/images/cars/`,
},
Then in pages/index.js it passes the Lightbox component all of the images.
<Lightbox images={data.allImageSharp.edges} />
allImageSharp {
edges {
node {
sizes(maxWidth: 1800) {
...GatsbyImageSharpSizes
}
}
}
}
So I'm trying to figure out how to provide it a custom set of images, based on different image directories.
I'm new to Gatsby and React and couldn't figure out how to filter allImageSharp by directory, but I did figure out a way with allFile...though I ran into a type error trying to use it.
What I tried:
gallery: allFile(filter: {relativeDirectory: {eq: "screenshots/producta"}, extension: {regex: "/(jpg)|(png)/"}}) {
edges {
node {
id
name
relativeDirectory
extension
}
}
}
But in trying to utilize that with:
<Lightbox images={props.data.gallery} />
I got the error "TypeError: images.map is not a function"
So I think I may have figured it out. What I've done appears to be working, but I would caution that I don't know how "right" or good of a solution it is.
I seemed to have been on a working path earlier in my question, but had a few things off.
Here's what I've done:
I updated my query so it would return childImageSharp for each node, so now it's returning the equivalent of what was being returned in with allImageSharp from the starter pack. However, this structure is slightly different, so I also had to update the Lightbox.js component to properly reference these changes. You'll notice that my query has a subfield called 'fluid', instead of 'sizes'. This is because I noticed 'sizes' is deprecated in Gatsby 2.x. Fluid appears to return the same fields though. So I updated 'sizes' to 'fluid' everywhere it was referenced in Lightbox.js.
<Lightbox images={props.data.gallery.edges} />
gallery: allFile(
filter: {
relativeDirectory: { eq: "screenshots/producta" }
extension: { regex: "/(jpg)|(png)/" }
}
) {
edges {
node {
id
name
relativeDirectory
extension
childImageSharp {
fluid {
base64
tracedSVG
aspectRatio
src
srcSet
srcWebp
srcSetWebp
sizes
originalImg
originalName
presentationWidth
presentationHeight
}
}
}
}
}
It's entirely possible to resolve several paths with gatsby-source-filesystem:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `cars`,
path: `${__dirname}/src/images/cars/`,
},
},
{
resolve: `gatsby-source-filesystem`,
options: {
name: `productScreenshots`,
path: `${__dirname}/src/screenshots/products/`,
},
},
Notice the name property? It could be used to query exactly that subset:
query getCarImages {
allFile(
filter: {sourceInstanceName: {eq: "cars"}}
) {
nodes {
childImageSharp {
fluid {
...
}
}
}
}
}
Alternatively, you can dump all your images into src/images, point plugin at it once and then filter based on subdirectories' paths as mentioned in your examples.
I have a rich text editor field that accepts an embedded block where the content type contains a reference link to another content type.
Like this:
content (rich text field)
- group (embedded block)
- group-items (reference field)
- item 1 (referenced content)
- item 2 (referenced content)
How can I get the referenced content items using #contentful/rich-text-react-renderer?
I currently have this:
import { MARKS, BLOCKS } from '#contentful/rich-text-types';
import { documentToReactComponents } from '#contentful/rich-text-react-renderer';
const options = {
renderNode: {
[BLOCKS.EMBEDDED_ENTRY]: (node) => {
console.log(node);
return true;
}
},
renderText: text => text.replace('!', '?'),
};
Which gives me a bunch of id's but not of the field data for the entries which is what I really want.
content: []
data:
target: {sys: {…}}
__proto__: Object
nodeType: "embedded-entry-block"
content: []
data:
target:
sys: {id: "c13cBu2W6nOkQMx6bsvqCE5", type: "Link", linkType: "Entry"}
__proto__: Object
__proto__: Object
nodeType: "embedded-entry-block"
__proto__: Object
There where 2 problems I was running into here.
Firstly coccasionally Gatsby's cache will cause issues retriving new data from contentful and as such you may only get sys not fields. Which is what was happening to me.
Just delete .cache and rerun yarn run dev and it should be good.
Secondly to get multiple contentTypes with enter blocks this can be achieved by looking for the Entry blocks sys.id. This way you can create different components to handle various content types.
[BLOCKS.EMBEDDED_ENTRY]: (node) => {
const fields = node.data.target.fields;
switch (node.data.target.sys.contentType.sys.id) {
case 'group-item':
return <div>
<GroupItem name={fields.name['en-US']} />
</div>
default:
return <div>Fallback Element</div>
}
I'm writing my first larger project in react and I need to set up markers in my map component. I've set everythin up as it is shown in the tutorial however it is not working correctly with my code and the markers are not shown on map.
const dummyGeoJson = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Point",
coordinates: [16.959285736083984, 52.40472293138462]
}
}
]
};
class EventMap extends React.Component {
componentDidMount() {
this.map = L.map("map", {
center: [51.9194, 19.1451],
zoom: 6
});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 20
}).addTo(this.map);
var geoJsonLayer = L.geoJSON().addTo(this.map);
geoJsonLayer.addData(dummyGeoJson);
}
render() {
return <Wrapper width="100%" height="800px" id="map" />;
}
}
From what i've read in official leaflet tutorial this code should create a new geojson layer and create a marker in a position referenced in geojson but actually the only thing that is shown is my tile layer.
You need to use a pointToLayer function in a GeoJSON options object when creating the GeoJSON layer like this:
componentDidMount() {
const map = L.map("map", {
center: [51.9194, 19.1451],
zoom: 6
});
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 20
}).addTo(map);
L.geoJSON(dummyGeoJson, {
pointToLayer: (feature, latlng) => {
return L.marker(latlng, { icon: customMarker });
}
}).addTo(map);
}
You can then pass a customMarker variable to define some options in order to make your marker be displayed on the UI
Demo
Welcome to SO!
The most probable reason is that you bundle your app (typically with webpack), but the build misses Leaflet default icon images.
So your Marker is there, but you cannot see it because its icon image is missing.
An easy way to debug it is to use another icon instead, as suggested in kboul's answer, or even more simply by using a CircleMarker.
Then to solve the issue of the build engine missing to process the default icon images, see Leaflet #4968:
explicitly import / require the Leaflet default icon images and modify the L.Icon.Default options to use the new imported paths
or use the leaflet-defaulticon-compatibility plugin (I am the author).