user92322
user92322

Reputation: 43

React Icon component with SVG's

I have a folder name icons that contains a lot of SVG files. Every file is an independent icon that looks like that:

icons/heart.svg (I want to avoid converting it to JS)

<svg height="24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M22.178 13.583l-9.131 8.992a1.502 1.502 0 0 1-2.094 0l-9.131-8.992a6.192 6.192 0 0 1 0-8.773c2.439-2.413 6.395-2.413 8.834 0L12 6.154l1.344-1.344c2.439-2.413 6.395-2.413 8.834 0a6.192 6.192 0 0 1 0 8.773"/></svg>

I want to create a component that will load an svg based on its name.

components/Icon.js

export const Icon = ({ name, ...props }) => {
  return (
    <svg height="24" width="24" viewBox="0 0 24 24" role="img">
      <path d={name} />
    </svg>
  );
};

Example usage I look for: (App.js)

<Icon name="heart" />  // should load heart.svg
<Icon name="star" /> // should load star.svg

Upvotes: 2

Views: 6171

Answers (3)

You don't have to convert SVG icon files to JS; it has already been done.

<svg-icon is=heart></svg-icon>
<svg-icon is=heart fill="green" scale=".8"></svg-icon>
<svg-icon is=heart fill="green" scale=".8" rotate="45"></svg-icon>
<svg-icon is=smile></svg-icon>
<svg-icon is=smile fill="gold" stroke="green" width="5"></svg-icon>

But.. it is a modern W3C standard Web Component <svg-icon is="heart"></svg-icon>

Alas React doesn't comply with modern Web Standards so you have to make it work in React.

For the IconMeister Web Component: https://iconmeister.github.io/
I converted 7300+ icons from major iconsets to JSON notation:

With this JSON (note you can specifiy different viewBoxes from different Icon Sets)

{ 
"heart" : "box:36;fill:red;path:M33 8c-1-3-5-5-10-4A10 10 0 0018 8A10 10 0 0013 4C8 3 
           4 5 3 8c-2 4-1 8 2 13c3 4 7 7 12 12a1 1 0 001 0c5-5 9-8 12-12c4-5 4-9 3-13z",
"smile": "box:128;fill:#000;path:M62 2C28 2 0 30 0 64s28 62 62 62s62-28 62-62S97 2 62 2z
          m0 112c-28 0-50-23-50-50S35 14 62 14s50 23 50 50s-23 50-50 50zm30-37c-3-3-7-2-9
          1c-6 7-13 10-21 10s-16-4-21-10c-3-3-6-3-9-1c-3 3-3 6-1 9c8 9 19 15 31 15s23-6 
          31-15c3-3 2-7-1-9zM42 60c5 0 8-4 8-8s-4-8-8-8s-8 4-8 8s4 8 8 8zm40-15c-7 0-14 
          5-15 11c-1 3 3 5 5 3l3-3c4-4 12-4 16 0l3 3c3 2 6 0 5-3c-1-7-9-11-15-11z"
}

The 796 Bytes! <svg-icon> Web Component uses standard HTML to create and control SVG:

<svg-icon is=heart></svg-icon>
<svg-icon is=heart fill="green" scale=".8"></svg-icon>
<svg-icon is=heart fill="green" scale=".8" rotate="45"></svg-icon>
<svg-icon is=smile></svg-icon>
<svg-icon is=smile fill="gold" stroke="green" width="5"></svg-icon>

and style:

<style>
  svg-icon {
    display:inline-block;
    width:120px;
    background:lightgreen;
  }
</style>

Outputs:

Full Web Component code:

No Libraries, No Frameworks, No external SVG files, No dependencies

Documentation and source at: https://iconmeister.github.io/

<style>
  svg-icon {
    display:inline-block;
    width:120px;
    background:lightgreen;
  }
</style>

<svg-icon is=heart></svg-icon>
<svg-icon is=heart fill="green" scale=".8"></svg-icon>
<svg-icon is=heart fill="green" scale=".8" rotate="45"></svg-icon>
<svg-icon is=smile></svg-icon>
<svg-icon is=smile fill="gold" stroke="green" width="5"></svg-icon>

<script>
((t,e={path:(t,e="")=>`<path d='${t}' ${e}/>`},i={stroke:"#000",rect:"<rect width='100%' height='100%' fill='{tile}' {border}/>",fill:"none",tile:"none",img:1,width:1,scale:1,opacity:1,is:"",border:"",filter:"",top:"",v1:"",v2:"",v3:"",box:9,rotate:0,xy:0,w:0,h:0,api:[t,e]})=>{customElements.define("svg-icon",class extends HTMLElement{static get observedAttributes(){return Object.keys(i)}attributeChangedCallback(){this.svg()}svg(s=this,r=s.A||Object.keys(s.A={...i}).map((t=>Object.defineProperty(s,t,{set:e=>s.setAttribute(t,e),get:()=>s.getAttribute(t)||getComputedStyle(s).getPropertyValue("--svg-icon-"+t).replace(/"/g,"").trim()||s.A[t]},e[t]=e=>(s.A[t]=e,"")))),l,a=(t[s.is]||"").split`;`.map((t=>([r,l]=t.trim().split`:`,e[r]?e[r].apply(s,l.split`,`):t))).join``,o=s.box/2,c=`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ${s.w||s.box} ${s.h||s.box}' style='vertical-align:top'>${s.rect}<g stroke-width='{width}' stroke='{stroke}' fill='{fill}' opacity='{opacity}' filter='{filter}' transform='translate({xy}) matrix({scale} 0 0 {scale} ${o-o*s.scale} ${o-o*s.scale}) rotate({rotate} ${o} ${o})'>${a}</g>${s.top}</svg>`.replace(/{\s?([^{}\s]*)\s?}/g,((t,e)=>s[e]))){return s.innerHTML=1==s.img?`<img style='vertical-align:top' src="data:image/svg+xml,${c.replace(/#/g,"%23")}">`:c}})})(
{"heart":"box:36;fill:red;path:M33 8c-1-3-5-5-10-4A10 10 0 0018 8A10 10 0 0013 4C8 3 4 5 3 8c-2 4-1 8 2 13c3 4 7 7 12 12a1 1 0 001 0c5-5 9-8 12-12c4-5 4-9 3-13z",
"smile":"box:128;fill:#000;path:M62 2C28 2 0 30 0 64s28 62 62 62s62-28 62-62S97 2 62 2zm0 112c-28 0-50-23-50-50S35 14 62 14s50 23 50 50s-23 50-50 50zm30-37c-3-3-7-2-9 1c-6 7-13 10-21 10s-16-4-21-10c-3-3-6-3-9-1c-3 3-3 6-1 9c8 9 19 15 31 15s23-6 31-15c3-3 2-7-1-9zM42 60c5 0 8-4 8-8s-4-8-8-8s-8 4-8 8s4 8 8 8zm40-15c-7 0-14 5-15 11c-1 3 3 5 5 3l3-3c4-4 12-4 16 0l3 3c3 2 6 0 5-3c-1-7-9-11-15-11z"});
</script>

PS. The source is UNlicensed, I would love to see a React version in 796 Bytes (GZipped)

Upvotes: 2

Yadab
Yadab

Reputation: 1883

You can create a file which exports all your svg file and give name to each one of them.

import heart from './icons/heart.svg'

export { heart };

Then you can import your icon anywhere you want according to the name;

import { heart } from './<path_of_above_file>'

if you want to create an Icon component then create a component and render the icon in there.

export const Icon = ({ icon, ...props }) => {
  return (
   <div {...props}>
     {icon}
   </div>
  );
};

Then use it anywhere you like.

import { heart } from './path_of_above_file';
<Icon icon={heart} />

Another solution is passing path as a prop;

export const Icon = ({ path, ...props }) => {
  return (
    <svg {...props} viewBox="0 0 24 24" role="img">
      <path d={path} />
    </svg>
  );
};

And use the icon passing the path as a prop.

<Icon path='M22.178 13.583l-9.131 8.992a1.502 1.502 0 0 1-2.094 0l-9.131-8.992a6.192 6.192 0 0 1 0-8.773c2.439-2.413 6.395-2.413 8.834 0L12 6.154l1.344-1.344c2.439-2.413 6.395-2.413 8.834 0a6.192 6.192 0 0 1 0 8.773' height="24" width="24" />

Upvotes: 6

Dennis Vash
Dennis Vash

Reputation: 53874

You can have an object to map path name to its value:

const PATHS = {
  star: "M22.178 ...",
  heart: "A22.153 ...",
};

export const Icon = ({ name, ...props }) => {
  return (
    <svg height="24" width="24" viewBox="0 0 24 24" role="img">
      <path d={PATHS[name]} />
    </svg>
  );
};

Upvotes: 0

Related Questions