In Posts / Guides / Gatsby • January 15th, 2020 • Gatsby, React

Using an SVG Favicon in Gatsby

Using an SVG favicon in a Gatsby site can be a bit tricky, because it is one of the only times where you don't want to inline the SVG.

My site generally uses gatsby-plugin-manifest for making rasterized favicons from my SVG file:

module.exports = {
    // ...
    plugins: [
        // ...
        {
            resolve: `gatsby-plugin-manifest`,
            options: {
                // ...
                icon: `src/images/favicon.svg`,
                icon_options: {
                    purpose: `maskable`,
                },
            },
        },
    ],
}

Even if the source image is an SVG, that plugin can't help with making the SVG source available on the site (even if you try to use its hybrid mode).

One potential solution would be to place a file in your public folder manually, commit it, and then hard-code a link to it in configuration for something like gatsby-plugin-react-helmet.

That solution is messy, though: in my case the entire public folder was generated and I didn't want to break that paradigm. What I wanted was to have static generation place my SVG favicon into the public folder automatically. Importing the favicon into a JavaScript file like you would any other image will actually inline it, because the file size is so small.

So how can you import an image without any risk of inlining it?

Gatsby (and GraphQL to the rescue): you can query the URL of a file and Gatsby will copy the file to public/static/ with a hashed filename. There's a few steps:

Make sure your icon can be accessed

Your icon will need to be in a folder read by an instance of gatsby-source-filesystem:

module.exports = {
    // ...
    plugins: [
        // ...
        {            resolve: `gatsby-source-filesystem`,            options: {                name: `images`,                path: `${__dirname}/src/images`,            },        },        {
            resolve: `gatsby-plugin-manifest`,
            options: {
                // ...
                icon: `src/images/favicon.svg`,
                icon_options: {
                    purpose: `maskable`,
                },
            },
        },
    ],
}

Query with GraphQL

You can craft a query using GraphiQL, but it might look something like this

query SvgIconQuery {
    allFile(
        limit: 1
        filter: {
            name: { eq: "favicon" }
            ext: { eq: ".svg" }
            sourceInstanceName: { eq: "images" }
            relativeDirectory: { eq: "" }
        }
    ) {
        edges {
            node {
                publicURL
            }
        }
    }
}

Make use of the URL

I use the children syntax for my usage of Helmet, so your usage might look a little different. You can look at the full example here on GitHub.

// In metadata.js, a React component I wrote
// to wrap Helmet:

function Metadata({ description, title }) {
    const { site, allFile } = useStaticQuery(
        graphql`
            query {
                site {
                    siteMetadata {
                        siteUrl
                        title
                        description
                        author
                        authorTwitter
                    }
                }
                allFile(                    limit: 1                    filter: {                        name: { eq: "favicon" }                        ext: { eq: ".svg" }                        sourceInstanceName: { eq: "images" }                        relativeDirectory: { eq: "" }                    }                ) {                    nodes {                        publicURL                    }                }            }
        `
    )

    return (
        <Helmet
        // ...
        >
            // ...
            <link                rel="icon"                href={allFile.nodes[0].publicURL}                type="image/svg+xml"                sizes="any"            />            // ...
        </Helmet>
    )
}

export default Metadata

Please let me know if you have any ideas, suggestions, or questions!

Light mode

Dark mode

Copyright 2020 Jack Warren