How can I display GIF images efficiently in my Gatsby blog website? - reactjs

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">

Related

NextJS + React + SVG Component - Import Typescript IntrinisicAttribues error

I'm trying to directly load SVG elements into a React component, and it's been a massive headache. I'm a bit new to really digging in with understanding. To be fair, I'm really enjoying Next, it reminds me a lot of Ruby or Laravel.
So, perhaps someone can help guide me on the path of enlightenment here... I'm using a Next plugin, withReactSvg
next.config.js
module.exports = withPlugins(
[
[
withReactSvg, {
include: path.resolve(__dirname, 'lib/svg/'),
webpack(config, options) {
return config
}
}
],
//[/*plug name*/, { /* plugin config here ... */ }],
],
{
/* global config here ... */
future: {
webpack5: true,
},
distDir: '.build',
},
);
This plugin extends Next to import SVGs more directly and be able to control internal SVG css more directly, and it's pretty elegant compared with other solutions...
In any component, I can now do the following:
ExampleComponent.tsx
import Icon from '../lib/svg/icon_4.svg';
export default function ExampleComponent() {
return ( <Icon fill="red" stroke="white" /> );
}
This code compiles and works, but trying to use Typescript I receive this error:
(JSX attribute) fill: string
Type '{ fill: string; stroke: string; }' is not assignable to type 'IntrinsicAttributes'.
Property 'fill' does not exist on type 'IntrinsicAttributes'.ts(2322)
The closest I think I've gotten to solving this puzzle is a really good article React Higher Order Component Patterns In Typescript, but it comes just shy of working because I'm sort of implicitly creating a react component with the import.
A lot of other articles seem to assume I'm trying to do something I shouldn't be doing... And maybe I am?

Import SVG as a component in Gatsby

I've seen the following solution:
import { ReactComponent as Img } from 'path/to/file.svg'
But in Gatsby, this doesn't work. I know exist plugins for this, but maybe it can be done more easily.
As you said, there are plugins to achieve this, which means a cleaner code (not the full SVG tag inlined in the component) with the same final result. Using gatsby-plugin-react-svg plugin you just need to import your SVG like this:
import Icon from "./path/assets/icon.svg";
To install, you only need to add the dependency using npm or yarn and in your gatsby-config.js use this snippet:
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /assets/
}
}
}
Note that /assets/ is an including rule based on a regular expression so the value must be your SVG folder (i.e: /svg/) and must only contain .svg files. In other words, if your path is /images/svg/ your including rule can only contain /svg/ (you can also add the full path but you'll need to escape slashes).
Afterward, you will need to style the SVG if their inline styles don't apply.
If you want to follow the non-plugin approach you can simply use a React-based approach, just creating a component that returns the SVG:
export const YourSvgComponent = () => (
<svg
version="1.1"
baseProfile="full"
width="300"
height="200"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">
SVG
</text>
</svg>
);
Now you just need to import it and use it.
Add new package:
npm install gatsby-plugin-react-svg
or
yarn add gatsby-plugin-react-svg
Configure at gatsby-config.js:
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /\.inline\.svg$/,
},
},
},
Here's an example of what this file might look like when completed
module.exports = {
siteMetadata: {
title: `Gatsby`,
},
plugins: [
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /assets/,
},
},
},
],
};
Rename your files to something like example.inline.svg
Import:
import Illustration from './illustration.inline.svg'
Usage:
<Illustration className="example" />
All the information from official Gatsby guide
Just do like this...
import YourDesiredName from 'path/to/file.svg'

Antd Theme is being overridden when used in Gatsby

I am currently trying to customize Antd theme by using the antd and less plugins for Gatsby. Following this thread - Change the Theme of Antd when using GatsbyJS
gatsby-config.js plugins contain
{
resolve: 'gatsby-plugin-antd',
options: {
style: true
}
},
{
resolve: `gatsby-plugin-less`,
options: {
javascriptEnabled: true,
modifyVars: {
'primary-color': '#BADA55'
}
}
}
gatsby-node.js and relevant modules are filled as seen in the thread.
When I look at an element in the browser debugger, the button uses the base theme, but the specified theme is actually there (just being overridden).
I had the same issue and for me it was that I had somewhere in my code imported the style for antd
import "antd/dist/antd.css"

How to modify Gatsby Starter Lightbox to query specific image directories?

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.

GeoJSON marker is not showing on my leafletjs map

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).

Resources