Import SVG as a component in Gatsby - reactjs

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'

Related

tailwind hover not working on my react app

I am working on magento 2 PWA using react and tailwind but all the classes in tailwind I can use but can't use hover.
Here is my tailwind.config.js:
// TODO #TW:
// Node path should be committed, but it makes preset dev impossible.
// Local path is the only way to develop "tailwind.preset.js".
const venia = require('#magento/pwa-theme-venia');
const config = {
mode: 'jit',
// Include your custom theme here.
presets: [venia],
// Configure how Tailwind statically analyzes your code here.
// Note that the Tailwind's `jit` mode doesn't actually use PurgeCSS.
purge: {
// Include paths to every file that may refer to Tailwind classnames.
// Classnames not found in these files will be excluded at build time.
content: [
'./src/**/*.{html,js}',
'./node_modules/#magento/venia-ui/lib/**/*.module.css',
'../venia-ui/lib/**/*.module.css',
'./src/**/*.module.css',
'./template.html'
],
theme: {
extend: {},
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px'
}
},
// Extract Tailwind classnames from source files.
// Our default matcher only matches targets of CSS Modules' `composes`,
// not classnames included directly in HTML or JS!
extractors: [
{
extensions: ['css', 'scss'],
extractor: content => content.match(matcher) || []
}
]
},
// Set the character Tailwind uses when prefixing classnames with variants.
// CSS Modules doesn't like Tailwind's default `:`, so we use `_`.
separator: '_'
};
module.exports = config;
/**
* Matches declarations that contain tailwind classnames.
* Only classnames matched by this expression will be included in the build.
*
* #example
* .foo {
* composes: mx-auto from global;
* }
*/
const matcher = /(?<=composes:.*)(\b\S+\b)(?=.*from global;)/g;
here is my postcss.config.js:
module.exports = {
plugins: [
require('autoprefixer'),
require('tailwindcss')('./tailwind.config.js')
]
};
How can I fix this issues???? Thank you so much!

Images getting wrapped in extra div when using combination of gatsby-plugin-mdx and gatsby-remark-images

I'm using the following versions:
"gatsby": "^4.21.1",
"gatsby-plugin-mdx": "^4.0.0",
"gatsby-remark-images": "^6.21.0",
"remark-unwrap-images": "^3.0.1",
And I have the following in place as part of my gatsby-config.js:
{
resolve: "gatsby-plugin-mdx",
options: {
gatsbyRemarkPlugins: [
{
resolve: "gatsby-remark-images",
options: {
linkImagesToOriginal: false,
backgroundColor: "transparent",
},
},
],
mdxOptions: {
remarkPlugins: [wrapESMPlugin("remark-unwrap-images")]
}
},
},
Then in my code I make use of the MDXProvider to override the rendering of some components like so:
<MDXProvider components={{ img: SomeComponent }}>
{children}
</MDXProvider>
Before upgrading to use the latest versions of gatsby-plugin-mdx and gatsby-remark-images this used to work nicely and I'd end up with my custom component being rendered correctly.
Since the update I'm finding that an extra div is wrapping the image which appears to be coming from gatsby-remark-images (although looking at the source code I can't see how or where).
As a result, this changes the behaviour of MDXProvider since it now thinks it's a div instead of an img which is a problem.
Has anyone encountered this or have a suggestion on how to resolve it?
Note: This is not the same as the issue with images getting wrapped in p Which can be resolved by using remark-unwrap-images per my gatsby-config.js as shown above. Also, further to this gatsby-remark-figure-caption does not appear to work in gatsby 4+.

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?

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

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

Module scss ampersand class selector not working

I have a simple scss file with
.navItem {
cursor: pointer;
&.active {
color: $primaryPurple;
}
&:hover {
color: $primaryPurple;
}
span {
margin-left: 10px;
}
}
For some reason, the :hover works but .active doesn't work. As you can see in the image below, li clearly has the active class but I don't see the font color css changed.
TL;DR: Pass the name as a JavaScript Object, not like a simple string.
Actually, this issue comes from the css-modules configuration. Obviously, based on the AdminSideBar_navItem__1aFBc in the embed picture. your css-modules configuration in the Webpack config is:
// webpack config
module.exports = {
~~~
module: {
~~~
rules: [
{
test: /\.scss$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
modules: true,
importLoaders: 1,
localIdentName: '[name]_[local]_[hash:base64:5]', // <=== Attention to this line
sourceMap: true,
}
},
~~~
],
}),
],
},
~~~
};
So, when you are using css-modules it means you will have so professional advantages like a more compressed css bundle file or full hashed unreadably uglified bundle. for these benefits, you should use CSS class names like a JavaScript object, pay attention to the following ReactJS code alongside using css-modules:
import React from 'react';
import cn from 'classnames'; // <=== install it to have easy coding
import styles from '[pathToScssFiles]/styles.scss';
const MyComponent = ({ isActive }) => (
<li className={cn(styles.navItem, isActive && styles.active)} />
);
I hope, you understand my point, you passed the styles.active as a simple string: "active". so css-modules cannot get your class name as an object and pass it exactly like what it got, as a simple string, hence, "active" **BUT** in the CSS bundle file the css-modulesuse the'[name][local][hash:base64:5]'` pattern to deform its name.
Your :hover should work and it works because anyone cannot change it, it's a pseudo-class.
For sure, write a weird property for .active:
.navItem {
cursor: pointer;
&.active {
fill: yellow;
}
Now seek for the fill: yellow in the CSS bundle that Webpack makes for you. you see it is:
.AdminSideBar_navItem__1aFBc .AdminSideBar_active__9be2a {
fill: yellow;
}
Note: the ~~~ means etc.
Providing you jsx code would help, but I will take a guess that you use active as a string literal, so your code looks like
import styles from './componentName.module.css';
// and then in the component
<li className={`nav-item ${styles.AdminSideBar_navItem} ${whateverCondition ? 'active' : ''}`}>
...children...
</li>
The problem here is that active class also has a suffix just like AdminSideBar_navItem. And in order to make it work you need to access it the same way you access AdminSideBar_navItem. So the code should be
<li className={`nav-item ${styles.AdminSideBar_navItem} ${whateverCondition ? styles.active : ''}`}>
...children...
</li>
and in html you will see
<li class="nav-item AdminSideBar_navItem_abcd active_bcde">
I dropped a simple example in this codesandbox, you can play around with it and see how it works.

Resources