4

I'm using rollup to bundle a react npm package that contains an icon component that takes a name as a prop and returns an Icon with that name wrapped by a react component. This is the component code:

    import sprite from './public/sprite.svg';
    
    function Icon({ name }) {
       return <svg className="svg-wrapper">
          <use href={`${sprite}#${name}`} />
       </svg>
      );
    }

The folder structure is the following:


     - src
     - - public 
     - - - sprite.svg
     - - icons
     - - - some-icon.svg
     - - - some-other-icon.svg
     - - index.tsx # component with the code mentioned above

And this is my rollup config:


    export default {
          plugins: [
            esbuild({
              sourceMap: false,
              target: "esnext"
            }),
            image(),
            svgicons({
              inputFolder: "src/icons",
              output: "public/sprite.svg"
            }),
            json()
         ]
        }

This works fine in Chrome (although it does inline all the svg inside of the href which I think it's the purpose of this approach) but in Safari it triggers the following error:

    Unsafe attempt to load URL data:image/svg+xml,%3c%3fxm ....
    Domains, protocols and ports must match.

The thing is, as mentioned, this is an npm package that packages the icons as part of the js bundle (inlining) so there's not much control over how this component is served since this is handled by the browser caching (also one of the key points of using this approach). I'm quite familiar with CORS and I know that perhaps avoiding to use data:image/svg+xml uri links would fix this but would increase the complexity of the build steps of this package (needing to build the icons using svgr/svgo and then have some kind of lookup table to give back the right icon based on the name prop i.e.).

So, ultimately my question is, with the sprite approach in a react component library is there a foolproof way of avoiding these kind of issues and cross-browser inconsistencies? Thanks in advance for any help provided.

Rafael Rocha
  • 508
  • 1
  • 3
  • 16
  • Are you running this from a web server or from the file system? – chrwahl Sep 06 '21 at 15:57
  • it's a react package, therefore it's being bundled together as the result of another application and then served via nginx. The issue isn't the web server, this happens later when using the `data:image/svg+xml` uri to load the asset from the browser cache. – Rafael Rocha Sep 06 '21 at 16:00
  • both locally and through a web server hits the same issue, again, only on Safari. – Rafael Rocha Sep 06 '21 at 16:01
  • 1
    Seems like a safari bug, try reporting it to safari's bugtracker. – Robert Longson Sep 06 '21 at 16:49
  • To rule out Safari , React or the Build process; Can you verify https://iconmeister.github.io/ produces the same error on Safari? It uses the same ``data:image`` URI method, but in vanilla JavaScript. – Danny '365CSI' Engelman Sep 18 '21 at 09:01

1 Answers1

1

I have been struggling with this issue for a while. I guess this is a bug on Safari throwing an error because is dealing with a dataURI as if it was an external URL.

About your code, you could expose your sprite in a public folder or publish it in a cdn (good for caching purposes) and change the way rollup is handling your svg (it seems it is packing your svg as a dataURI). Alternatively, I implemented a workaround to convert the dataURI in a blob.

import sprite from './public/sprite.svg';

function dataURItoBlobUrl(dataURI: string) {
  const svg = decodeURI(dataURI).split(',')[1];
  const blob = new Blob([svg], { type: "image/svg+xml" });

  return URL.createObjectURL(blob);
}

const blobUrl = dataURItoBlobUrl(sprite);

export const Icon: FC<IconProps> = ({ name, ...props }) => {
  return (
    <svg xmlns="http://www.w3.org/2000/svg" {...props}>
      <use href={`${blobUrl}#${name}`}></use>
    </svg>
  );
};
bor21ja
  • 11
  • 1
  • 1
  • I ended up using svgr to generate the svg into react components and then i use a lookup by key from the whole module import - since the result of an import is an object. Since we are following a tree shakeable approach it has minimal impact on performance. but yes ideally it should be exposed through a CDN. Thanks for your input! – Rafael Rocha Jan 24 '22 at 14:43