I created some SVG sprites/symbols to be able to use icon as component and with gatsby develop and it works fine (img below).
Problem is when i try to build my Gatsby application with gatsby build SVG icons are not showing (img below).
Anyone has idea what am I missing?
My Icon component:
import { IconTypes } from "./types";
import Icons from "./icons.svg";
interface Props {
name: IconTypes;
className?: string;
width?: number;
height?: number;
fill?: string;
}
export const Icon: FC<Props> = ({ name, className, width, height, fill }) => {
return (
<svg className={className} width={width} height={height} fill={fill}>
<use href={`${Icons}#${name}`} />
</svg>
);
};
Using SVGs in Gatsby + React could be tricky. I would suggest using gatsby-plugin-react-svg since it controls all the fallbacks. In your gatsby-config.js:
plugins: [
{
resolve: "gatsby-plugin-react-svg",
options: {
rule: {
include: /svg/
}
}
}
];
Note that the including rule is a regular expression. It means that /svg/ folder is the folder path in all your project (i.e: if you have your SVGs inside /src/assets/svg in the plugin configuration must include only /svg/).
The rest of the configuration is similar to the one you've described:
import Icon from "./path/assets/icon.svg";
// ...
<Icon />;
Related
I have created a dynamic import to use icons from our library. It looks something like this:
// icons file
export { ArrowLeft } from './ArrowLeft';
export { ArrowRight } from './ArrowRight';
export { Checked } from './Checked';
export { CircleCar } from './CircleCar';
And this is the component:
import * as Icons from '../../components/Icons';
import * as z from 'zod';
const Schema = z.object({
content: z.object({
iconName: z.string()
})
})
type JustAReactComponentProps = z.infer<typeof Schema>
const JustAReactComponent = ({content}: JustAReactComponentProps) => {
const SecondaryIcon = Icons[content.iconName] as React.JSXElementConstructor<any>;//is this the correct type btw?
return <SecondaryIcon />
}
And lets say that we have 30 icons and someone tries to set iconName as an icon that is not in the list, how can I enforce with Typescript that only the ones in the icons file are enabled to be used?
This question below seems to have the same problem I do but the solutions do not work for me(And I'm not looking for an external package).
How to find all imported (in code) dependencies within a TypeScript project?
I am creating a custom UI library for work that uses Material-UI. The UI library has a custom theme where I added to the palette object with custom company colors. The theme lives in the UI library, and for elements that live there, I can use the custom colors in makeStyles, but when I try to use the exported theme in the main codebase, the custom colors throw errors in makeStyles.
I believe the issue is I'm not exporting the theme's custom module. I am unsure how to export and import this correctly into the main codebase. Currently, I am only exporting the theme file and importing it into the main codebase.
Main Codebase:
import { theme } from 'customUILibrary';
...
<MuiThemeProvider theme={theme}>
...
</MuiThemeProvider>
CustomUILibrary:
index.ts:
export { theme } from 'theme/theme';
theme/theme.ts
import {
createTheme,
responsiveFontSizes,
} from '#material-ui/core/styles';
import './extendPalette';
export const theme = responsiveFontSizes(createTheme({
palette: {
gradients: {
primary: 'linear-gradient(270deg, #35C7E1 -10.76%, #1A92BD 121.8%)',
secondary: 'linear-gradient(270deg, #194E94 0%, #317CA6 100%)',
},
},
}));
extendPalette.ts
declare module '#material-ui/core/styles/createPalette' {
export interface PaletteOptions {
gradients: {
primary: string
secondary: string,
},
}
export interface Palette {
gradients: {
primary: string
secondary: string,
},
}
}
The custom theme attributes work great inside the UI Library with the UI elements, but when imported into the main codebase the custom attributes aren't being picked up, and in fact causing errors.
As suggested here, you can do something like this:
declare module "#mui/material/styles" {
interface Palette {
custom: {
pink: string;
};
}
interface PaletteOptions {
custom: {
pink: string;
};
}
}
Then you can set you value in theme safely:
const theme = createTheme({
palette: {
custom: {
pink: "pink"
}
}
});
and use it in styles:
<Button sx={{ color: "custom.pink" }}>Content</Button>
See my codesandbox example
Is it possible to access the current page's frontmatter through the default layout.
Using graphql, is there an option to filter a query based on the page's url?
.MDX Frontmatter
---
Title: 'About Us'
---
gatsby-config.js
{
resolve: 'gatsby-plugin-mdx',
options: {
defaultLayouts: {
pages: require.resolve('./src/layouts/default.js'),
},
},
}
default.js
<>
<header>?? Insert MDX Title ??</header>
<main></main>
<footer></footer>
</>
Even if you are generating pages from ./src/pages/ automatically using a default MDX template you still need to add the following to your plugins section in gatsby-config.js:
{
resolve: `gatsby-source-filesystem`,
options: {
name: `pages`,
path: `${__dirname}/src/pages/`,
}
}
Now you can access the frontmatter with pageContext.frontmatter as mentioned in the documentation i.e.:
import React from 'react'
import Layout from './layout'
export default ({ children, pageContext }) => {
return (
<Layout>
<h1>{pageContext.frontmatter.title}</h1>
<article>{children}</article>
</Layout>
)
}
Yes there is. One way to step through it is to run gatsby develop and run your queries against the GraphiQL instance that gets ran. I have the title in my .mdx files and it is available through the allMdx and mdx schema. The Gatsby documentation here explains how to set up your component with the query.
{
allMdx {
edges {
node {
frontmatter {
title
}
}
}
}
mdx {
frontmatter {
title
}
}
}
MDX frontmatter is passed automatically along with children into the parent context.
{props.pageContext.frontmatter.Title}
I am wondering how to set up an inline svg with webpack?
I am following the react-webpack-cookbook.
I have my webpack.config set up correctly with the file loader.
However the example shows using a background image like this:
.icon {
background-image: url(./logo.svg);
}
which works fine, but I want to have an inline svg image how do I do this to include my logo.svg inline in my react component?
import React, { Component } from 'react'
class Header extends Component {
render() {
return (
<div className='header'>
<img src={'./logo.svg'} />
</div>
);
}
};
export default Header
Actually Michelle's answer pointed me in the right direction, and that works nicely for loading an svg file with webpack and using it as your <img> src
However to actually get the inline svg, I needed to do the following:
Instead of file-loader use svg-inline-loader as your svg loader:
{ test: /\.svg$/, loader: 'svg-inline-loader' }
Then to load the svg inline in a component:
import React, { Component } from 'react'
import logo from "./logo.svg";
class Header extends Component {
render() {
return (
<div className='header'>
<span dangerouslySetInnerHTML={{__html: logo}} />
</div>
);
}
};
export default Header
It looks like there is an inline svg wrapper for react svg-inline-react which would be another option instead of the <div dangerouslySetInnerHTML={{__html: mySvg}} />
Here is a simple non-react solution.
Install Svg inline loader
In webpack.config.js add { test: /\.svg$/, loader: 'svg-inline-loader' }
In your js file import svg image and add it to a DOM element like so
import Svg from './svg.svg';
function component() {
const element = document.createElement('div');
element.innerHTML = Svg;
return element;
}
document.body.appendChild(component());
I hope my late answer will still be useful for someone, because I don't like any of abovementioned options.
The react-svg-loader webpack loader allows you to import SVG icons like JSX components:
import Logo from './logo.svg';
class App extends Component {
render() {
return (
<div className="App">
<Logo fill="red" className="logo" width={50} height={50} />
</div>
);
}
}
and minimum config looks like this:
{
test: /\.svg$/,
use: [
{
loader: "babel-loader"
},
{
loader: "react-svg-loader",
options: {
jsx: true // true outputs JSX tags
}
}
]
}
The best part is that it just outputs the svg file contents, without any extra wrappers and dangerouslySetInnerHTML in your code.
Old question, but I didn't see this solution anywhere so I decided to post it, hoping it will help someone.
If you want to be able to style those SVG icons, you might want to load them with the raw loader:
webpack.config.js:
{
test: /\.svg$/,
loader: 'raw-loader'
}
The import in my view:
import closeIcon from 'svg/ic_close_black_24px.svg';
The template (Mustache uses 3 brackets to insert the SVG data (URL)unencoded):
<button id="closeModal">
{{{closeIcon}}}
</button>
this way the SVG data will be inserted instead of the brackets and look like this:
<button id="closeModal">
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</button>
I'm using Backbone with Mustache template engine with Webpack 2.5.1
If I'm not mistaken, since you're using the file loader, you can utilize it in much the same way as any other require. Webpack will turn require("./logo.svg") into a path to a file, which it will emit when it bundles.
import React, { Component } from 'react'
import mySvg from './logo.svg'
class Header extends Component {
render() {
return (
<div className='header'>
<img src={mySvg} />
</div>
);
}
};
export default Header
Similar to another answer using React, there is also a handy Vue plugin as well.
vue-svg-loader just throw it in your configuration and start using. The nice thing is it will also run your svg through SVGO to optimize it.
Configuration
{
test: /\.svg$/,
loader: 'vue-svg-loader', // `vue-svg` for webpack 1.x
options: {
// optional [svgo](https://github.com/svg/svgo) options
svgo: {
plugins: [
{removeDoctype: true},
{removeComments: true}
]
}
}
}
Usage
<template>
<nav id="menu">
<a href="...">
<SomeIcon class="icon" />
Some page
</a>
</nav>
</template>
<script>
import SomeIcon from './assets/some-icon.svg';
export default {
name: 'menu',
components: {
SomeIcon,
},
};
</script>
Angular Solution (2019): Use svg-sprite-loader to combine SVGs into a single sprite that's lazy-loaded with your Webpack bundles.
Webpack
{
test: /\.svg$/,
use: [
'svg-sprite-loader',
'svgo-loader' // Optimize SVGs (optional)
]
}
HTML
<svg>
<use xlink:href="#arrow"/>
</svg>
Angular Component
export * from 'assets/images/icons/arrow.svg';
I use export (instead of import) to prevent the AOT compiler from removing the import during tree-shaking, while allowing for minimal code in the component, but you can use import if you prefer.
To use export in this way, you must configure the compiler to expect side effects from SVG files in package.json (i.e. you can not use "sideEffects": false). See the Webpack Tree Shaking Guide
"sideEffects": [
"*.svg",
],
#svgr/webpack (npm) is the inline svg loader that create-react-app uses.
Add the rule to your webpack config:
{
test: /\.svg$/,
use: ['#svgr/webpack'],
}
Then you can import svgs as React components:
import Star from './star.svg'
const App = () => (
<div>
<Star />
</div>
)
Folks who use svg-inline-loader and who stuck with "Cannot find module" error try to install babel-plugin-inline-react-svg and add it to the babel plugins:
"plugins": [
...
["inline-react-svg", {}]
],
...
I am wondering how to set up an inline svg with webpack?
I am following the react-webpack-cookbook.
I have my webpack.config set up correctly with the file loader.
However the example shows using a background image like this:
.icon {
background-image: url(./logo.svg);
}
which works fine, but I want to have an inline svg image how do I do this to include my logo.svg inline in my react component?
import React, { Component } from 'react'
class Header extends Component {
render() {
return (
<div className='header'>
<img src={'./logo.svg'} />
</div>
);
}
};
export default Header
Actually Michelle's answer pointed me in the right direction, and that works nicely for loading an svg file with webpack and using it as your <img> src
However to actually get the inline svg, I needed to do the following:
Instead of file-loader use svg-inline-loader as your svg loader:
{ test: /\.svg$/, loader: 'svg-inline-loader' }
Then to load the svg inline in a component:
import React, { Component } from 'react'
import logo from "./logo.svg";
class Header extends Component {
render() {
return (
<div className='header'>
<span dangerouslySetInnerHTML={{__html: logo}} />
</div>
);
}
};
export default Header
It looks like there is an inline svg wrapper for react svg-inline-react which would be another option instead of the <div dangerouslySetInnerHTML={{__html: mySvg}} />
Here is a simple non-react solution.
Install Svg inline loader
In webpack.config.js add { test: /\.svg$/, loader: 'svg-inline-loader' }
In your js file import svg image and add it to a DOM element like so
import Svg from './svg.svg';
function component() {
const element = document.createElement('div');
element.innerHTML = Svg;
return element;
}
document.body.appendChild(component());
I hope my late answer will still be useful for someone, because I don't like any of abovementioned options.
The react-svg-loader webpack loader allows you to import SVG icons like JSX components:
import Logo from './logo.svg';
class App extends Component {
render() {
return (
<div className="App">
<Logo fill="red" className="logo" width={50} height={50} />
</div>
);
}
}
and minimum config looks like this:
{
test: /\.svg$/,
use: [
{
loader: "babel-loader"
},
{
loader: "react-svg-loader",
options: {
jsx: true // true outputs JSX tags
}
}
]
}
The best part is that it just outputs the svg file contents, without any extra wrappers and dangerouslySetInnerHTML in your code.
Old question, but I didn't see this solution anywhere so I decided to post it, hoping it will help someone.
If you want to be able to style those SVG icons, you might want to load them with the raw loader:
webpack.config.js:
{
test: /\.svg$/,
loader: 'raw-loader'
}
The import in my view:
import closeIcon from 'svg/ic_close_black_24px.svg';
The template (Mustache uses 3 brackets to insert the SVG data (URL)unencoded):
<button id="closeModal">
{{{closeIcon}}}
</button>
this way the SVG data will be inserted instead of the brackets and look like this:
<button id="closeModal">
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
<path d="M0 0h24v24H0z" fill="none"></path>
</svg>
</button>
I'm using Backbone with Mustache template engine with Webpack 2.5.1
If I'm not mistaken, since you're using the file loader, you can utilize it in much the same way as any other require. Webpack will turn require("./logo.svg") into a path to a file, which it will emit when it bundles.
import React, { Component } from 'react'
import mySvg from './logo.svg'
class Header extends Component {
render() {
return (
<div className='header'>
<img src={mySvg} />
</div>
);
}
};
export default Header
Similar to another answer using React, there is also a handy Vue plugin as well.
vue-svg-loader just throw it in your configuration and start using. The nice thing is it will also run your svg through SVGO to optimize it.
Configuration
{
test: /\.svg$/,
loader: 'vue-svg-loader', // `vue-svg` for webpack 1.x
options: {
// optional [svgo](https://github.com/svg/svgo) options
svgo: {
plugins: [
{removeDoctype: true},
{removeComments: true}
]
}
}
}
Usage
<template>
<nav id="menu">
<a href="...">
<SomeIcon class="icon" />
Some page
</a>
</nav>
</template>
<script>
import SomeIcon from './assets/some-icon.svg';
export default {
name: 'menu',
components: {
SomeIcon,
},
};
</script>
Angular Solution (2019): Use svg-sprite-loader to combine SVGs into a single sprite that's lazy-loaded with your Webpack bundles.
Webpack
{
test: /\.svg$/,
use: [
'svg-sprite-loader',
'svgo-loader' // Optimize SVGs (optional)
]
}
HTML
<svg>
<use xlink:href="#arrow"/>
</svg>
Angular Component
export * from 'assets/images/icons/arrow.svg';
I use export (instead of import) to prevent the AOT compiler from removing the import during tree-shaking, while allowing for minimal code in the component, but you can use import if you prefer.
To use export in this way, you must configure the compiler to expect side effects from SVG files in package.json (i.e. you can not use "sideEffects": false). See the Webpack Tree Shaking Guide
"sideEffects": [
"*.svg",
],
#svgr/webpack (npm) is the inline svg loader that create-react-app uses.
Add the rule to your webpack config:
{
test: /\.svg$/,
use: ['#svgr/webpack'],
}
Then you can import svgs as React components:
import Star from './star.svg'
const App = () => (
<div>
<Star />
</div>
)
Folks who use svg-inline-loader and who stuck with "Cannot find module" error try to install babel-plugin-inline-react-svg and add it to the babel plugins:
"plugins": [
...
["inline-react-svg", {}]
],
...