Gatsby: Svg images as props

Created on 15 Feb 2019  路  7Comments  路  Source: gatsbyjs/gatsby

Hi, I've got a question how can I send SVG images as props?

If I do this I can display the image without any problem.


import ihtml from '../images/html5.svg'

const Skill = () => (
        <div>
            <img src={ihtml} alt=""/>
        </div>  
)

But I can't display an image as a prop like this :

import ihtml from '../images/html5.svg'

const Skill = (props) => (
    <div>
            {
                props.skillNames.map((skillName, index) => {
                    return (
                        <div key={shortid.generate()}>
                            // the value of skillName.icon is ihtml
                            <img src={skillName.icon} alt=""/>
                        </div>
                    )
                })
            }
    </div>

)

Any idea of how can I display images as props?

Thanks

question or discussion

Most helpful comment

@Alfrex92 i've picked up on your issue and it looks like there's a bit of confusion regarding on how Gatsby handles assets.
By looking at your code, it looks like Gatsby and webpack will not "understand" and resolve the import of the assets you want.

You can avoid this through a couple of ways:

  • One is to use the static folder approach, more on that issue here
  • Use the gatsby-source-filesystem plugin, coupled with a graphql query to fetch the contents needed.

Below will be demonstrated both approaches.

This are based on a new Gatsby site, created with the hello world starter.

For the static folder approach here are the steps i took and code:

  • After the setup process of the template is complete, created the folder and grabbed a couple of svg files into it. Which originates the following structure:
    static_folder_content

  • After that i've created a component named Skill under src/components with the following code:

import React from 'react'
import uuid from 'uuid'
import { withPrefix } from 'gatsby';
const Skill = props => {
  const { skillNames } = props
  return (
    <div>
      {skillNames.map(item => {
        return (
          <div key={`svg_image_container${uuid.v4()}`}>
            <img
              key={`svg_image_${uuid.v4()}`}
              src={withPrefix(item.icon)}
              alt={item.title}
            />
          </div>
        )
      })}
    </div>
  )
}
export default Skill

As you can see it's almost identical to the one you have, the only exception is uuid package that i've added to generate a unique uuid for the keys.
Also the withPrefix import that will handle the "relativity" of the files.

  • Adjusted the index.js file under pages to the following:
import React from 'react'
import Skill from '../components/Skill'

export default () => {
  const svgInfo = [
    {
      icon: './images/beacon.svg',
      title: 'beacon',
    },
    {
      icon: './images/osi.svg',
      title: 'osi',
    },
    {
      icon: './images/pencil.svg',
      title: 'pencil',
    },
    {
      icon: './images/semweb.svg',
      title: 'semweb',
    },
    {
      icon: './images/star.svg',
      title: 'star',
    },
    {
      icon: './images/sync.svg',
      title: 'sync',
    },
    {
      icon: './images/unicode.svg',
      title: 'unicoder',
    },
    {
      icon: './images/vnu.svg',
      title: 'vnu',
    },
    {
      icon: './images/vote.svg',
      title: 'voting',
    },
    {
      icon: './images/whatwg.svg',
      title: 'what',
    },
    {
      icon: './images/why.svg',
      title: 'why',
    },
    {
      icon: './images/wireless.svg',
      title: 'wireless',
    },
  ]

  return <Skill skillNames={svgInfo} />
}

As the static folder exists and is populated when each and every item of the array is iterated and rendered, Gatsby will understand the file location and show you the contents, as you can see below:
rendered_static

To achieve the same result, but this time with the gatsby-source-filesystem plugin like i said above, some changes are needed:

  • Install the gatsby-source-filesystem.
  • Create/modify your gatsby-config.js file to configure the plugin, based on the information you've supplied it would look like something like this:
module.exports = {
  siteMetadata: {
    title: `svg images as props`,
    description: `This barebones site demonstrates how to add svg images`,
  },
  plugins: [{
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}
  • Copied over the svg files to the folder src/images, as you can see below
    images_under_src

  • Created a new component named Skill-Graphql for this case to diferentiate it, with the following code:

import React from 'react'
import uuid from 'uuid'

const SkillGraphql = props => {
  const { skillNames } = props
  return (
    <div>
      {skillNames.map(item => {
        return (
          <div key={`svg_image_container${uuid.v4()}`}>
            <img
              key={`svg_image_${uuid.v4()}`}
              src={item.node.publicURL}
              alt={`svg_${item.node.publicURL}`}
            />
          </div>
        )
      })}
    </div>
  )
}
export default SkillGraphql

And finally a new page with the following code:

import React from 'react';
import { graphql } from "gatsby"
import SkillGraphql from '../components/Skill-Graphql'

const graphqlSourcePage=({ data })=>{
    return (
        <SkillGraphql skillNames={data.allFile.edges}/>
    )
}
export const query=graphql`
    query {
        allFile(filter: {extension: {eq: "svg"}}) {
            edges {
                node {
                    publicURL
                }
            }
        }         
    }
`;
export default graphqlSourcePage

As you can see the svg are loaded if i head onto page-2(file was called that, but the export is graphqlSourcePage), i'm presented with the following:
graphql_import_svg

Also sorry for the svg shown in the images not being resized, forgot about it.
Hope i could shed some light on your issue with this rather extensive comment.
Feel free to provide feedback.

All 7 comments

Are there any errors? Either in terminal running gatsby build/develop or in browser? What is result of using your second code snippet?

Are there any errors?
No

Either in the terminal running, gatsby build/develop or in the browser?
gatsby develop

What is the result of using your second code snippet?

<img src="ihtml" alt="">

@Alfrex92 i've picked up on your issue and it looks like there's a bit of confusion regarding on how Gatsby handles assets.
By looking at your code, it looks like Gatsby and webpack will not "understand" and resolve the import of the assets you want.

You can avoid this through a couple of ways:

  • One is to use the static folder approach, more on that issue here
  • Use the gatsby-source-filesystem plugin, coupled with a graphql query to fetch the contents needed.

Below will be demonstrated both approaches.

This are based on a new Gatsby site, created with the hello world starter.

For the static folder approach here are the steps i took and code:

  • After the setup process of the template is complete, created the folder and grabbed a couple of svg files into it. Which originates the following structure:
    static_folder_content

  • After that i've created a component named Skill under src/components with the following code:

import React from 'react'
import uuid from 'uuid'
import { withPrefix } from 'gatsby';
const Skill = props => {
  const { skillNames } = props
  return (
    <div>
      {skillNames.map(item => {
        return (
          <div key={`svg_image_container${uuid.v4()}`}>
            <img
              key={`svg_image_${uuid.v4()}`}
              src={withPrefix(item.icon)}
              alt={item.title}
            />
          </div>
        )
      })}
    </div>
  )
}
export default Skill

As you can see it's almost identical to the one you have, the only exception is uuid package that i've added to generate a unique uuid for the keys.
Also the withPrefix import that will handle the "relativity" of the files.

  • Adjusted the index.js file under pages to the following:
import React from 'react'
import Skill from '../components/Skill'

export default () => {
  const svgInfo = [
    {
      icon: './images/beacon.svg',
      title: 'beacon',
    },
    {
      icon: './images/osi.svg',
      title: 'osi',
    },
    {
      icon: './images/pencil.svg',
      title: 'pencil',
    },
    {
      icon: './images/semweb.svg',
      title: 'semweb',
    },
    {
      icon: './images/star.svg',
      title: 'star',
    },
    {
      icon: './images/sync.svg',
      title: 'sync',
    },
    {
      icon: './images/unicode.svg',
      title: 'unicoder',
    },
    {
      icon: './images/vnu.svg',
      title: 'vnu',
    },
    {
      icon: './images/vote.svg',
      title: 'voting',
    },
    {
      icon: './images/whatwg.svg',
      title: 'what',
    },
    {
      icon: './images/why.svg',
      title: 'why',
    },
    {
      icon: './images/wireless.svg',
      title: 'wireless',
    },
  ]

  return <Skill skillNames={svgInfo} />
}

As the static folder exists and is populated when each and every item of the array is iterated and rendered, Gatsby will understand the file location and show you the contents, as you can see below:
rendered_static

To achieve the same result, but this time with the gatsby-source-filesystem plugin like i said above, some changes are needed:

  • Install the gatsby-source-filesystem.
  • Create/modify your gatsby-config.js file to configure the plugin, based on the information you've supplied it would look like something like this:
module.exports = {
  siteMetadata: {
    title: `svg images as props`,
    description: `This barebones site demonstrates how to add svg images`,
  },
  plugins: [{
      resolve: `gatsby-source-filesystem`,
      options: {
        name: `images`,
        path: `${__dirname}/src/images`,
      },
    },
    // this (optional) plugin enables Progressive Web App + Offline functionality
    // To learn more, visit: https://gatsby.app/offline
    // 'gatsby-plugin-offline',
  ],
}
  • Copied over the svg files to the folder src/images, as you can see below
    images_under_src

  • Created a new component named Skill-Graphql for this case to diferentiate it, with the following code:

import React from 'react'
import uuid from 'uuid'

const SkillGraphql = props => {
  const { skillNames } = props
  return (
    <div>
      {skillNames.map(item => {
        return (
          <div key={`svg_image_container${uuid.v4()}`}>
            <img
              key={`svg_image_${uuid.v4()}`}
              src={item.node.publicURL}
              alt={`svg_${item.node.publicURL}`}
            />
          </div>
        )
      })}
    </div>
  )
}
export default SkillGraphql

And finally a new page with the following code:

import React from 'react';
import { graphql } from "gatsby"
import SkillGraphql from '../components/Skill-Graphql'

const graphqlSourcePage=({ data })=>{
    return (
        <SkillGraphql skillNames={data.allFile.edges}/>
    )
}
export const query=graphql`
    query {
        allFile(filter: {extension: {eq: "svg"}}) {
            edges {
                node {
                    publicURL
                }
            }
        }         
    }
`;
export default graphqlSourcePage

As you can see the svg are loaded if i head onto page-2(file was called that, but the export is graphqlSourcePage), i'm presented with the following:
graphql_import_svg

Also sorry for the svg shown in the images not being resized, forgot about it.
Hope i could shed some light on your issue with this rather extensive comment.
Feel free to provide feedback.

@jonniebigodes thank you sooo much for the detailed explanation. I understand it perfectly, I appreciated it =D

It is working.

I have one more question. One of the features that I like most about Gatsby is that automatically converts my img to data URI. With these two methods that you explained to me my img are not being converted to data URI. Is there any way where I can pass images as props and automatically converted it to data URI?

*As feedback, might be a good idea to add your explanation in this page. It was clear and easy to understand, I am sure that someone else will have the same question.
https://www.gatsbyjs.org/docs/adding-images-fonts-files/

@Alfrex92 No need to thank, i'm glad i could help you out and shed some insights to answer your issue. To answer your question regarding data uris, out of the box no. you can't. For that to work you would have to open each file, convert it to base64 format, escape it's content and suplly it to the <img/> element.

What you might be accustomed to see with gatsby and the images being shown as data uris, is that
gatsby-image component which relies on gatsby-transformer-sharp and gatsby-plugin-sharp handles that for you, it creates the traced image as a svg behind the scenes and lets you use it as so. Now here's the thing, that will only work for images like .jpg or .png for instance. For svgs it will not work, as this case the images are vector files and they don't need diferent sizes, they can scale indefinitely without loss of quality.

Regarding your second item, the feedback. If you want to do it, contribute to the documentation with this addition, i'm more than happy that you do it, grab the explanation i've provided, let me know and i'll set up a github repo with the code i've used for your issue and proceed from there. Sounds good?

I got it, thanks for the explanation :D

@Alfrex92 Once again, no need to thank, i'm glad i could help. Going to close this issue as it's answered. Once again, if you're willing to make that addition to the documentation let me know.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

magicly picture magicly  路  3Comments

brandonmp picture brandonmp  路  3Comments

kalinchernev picture kalinchernev  路  3Comments

jimfilippou picture jimfilippou  路  3Comments

3CordGuy picture 3CordGuy  路  3Comments