2

I am building an interface to preview animations applied to SVGs. I am trying to target an SVG's internal paths for animation with the Velocity.js library, in React. I'm using create-react-app.

I am able to drag-and-drop a .svg file into a react-dropzone input, which provides a callback with a File object. I can convert the File object to a base64 encoded DataURL, which I can then provide to an <object> tag. This renders the SVG with the full, expanded xml markup.

return (
    <object style={{
        width: '100%'
    }}
    id={`svg-${uuid}`} 
    type="image/svg+xml" data={dataURL}
/>)

Any attempt to access the contentDocument of the SVG, using javascript, (for the purposes of targeting g and path elements directly for animation) results in the following error:

document.getElementById('svg-665790ae-2a36-446e-9ea9-856442e8b1dc').contentDocument

Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLObjectElement': 
Blocked a frame with origin "http://localhost:3000" from accessing a cross-origin frame.

I am working on a local development environment, serving the React application on localhost:3000.

As a possible workaround, I uploaded the SVG in question to a remote AWS S3 storage bucket. I then retrieve the SVG via the remote URL, and am trying to render that, in some way. I have tried the following approaches:

React SVG <use> tag

<svg>
     <use xlinkHref={url}></use>
</svg>

This results in the following markup, but no visible SVG:

<svg>
    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://s3.amazonaws.com/.../example.svg">
    </use>
</svg>

<object> tag

<object
    style={{
        width: '100%',
    }}
    id={`svg-${uuid}`} type="image/svg+xml" data={url}
/>

This results in the following markup, without a visible SVG, and it causes the SVG to be immediately downloaded as a file by the browser.

<object id="svg-..." type="image/svg+xml" data="https://s3.amazonaws.com/.../example.svg" style="width: 100%;"></object>

react-inlinesvg library

This library makes an XHR request to retrieve the SVG and then embeds it into the DOM.

<Isvg src={url}/>

This results in the following error:

XMLHttpRequest cannot load https://s3.amazonaws.com/.../example.svg. 
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:3000' is therefore not allowed access.

SVGInjector

First I mounted a simple <img> tag with the SVG url:

return (
    <img
        ref={node => this.testSVG = node}
        src={url} id="svg" type="image/svg+xml" />
)

This also results in the above XMLHttpRequest error. Attempts to call SVGInjector, from componentDidMount, with either the node ref (i.e., SVGInjector(this.testSVG)), or in this manner:

const el = document.getElementById('svg');
SVGInjector(el);

causes the warning to disappear but results in only a broken image tag displaying.

Any help in resolving these CORS warnings or guidance on the correct way to embed SVG in the DOM, in React, for the purposes of animation, would be greatly appreciated. Perhaps what I need to do is download the image via the server, and temporarily serve it directly from the server filesystem, e.g., from the /public folder. This is not my preferred approach, as I am already using AWS S3 for image hosting.

andrewhl
  • 992
  • 1
  • 11
  • 31
  • 1
    If you control the server from which your code is trying to load images cross-origin (`https://s3.amazonaws.com/.../example.svg`), you need to configure that server to include the Access-Control-Allow-Origin response header when it serves those images. If you don’t control that server, then you must use some kind of proxy—either something you set up on your own backend, or else an open CORS proxy. See the *How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems* section of the answer at https://stackoverflow.com/a/43881141/441757 for example code+explanation – sideshowbarker Sep 09 '17 at 13:56
  • 1
    Thank you! That appears to have done the trick. I can now use the react-inlinesvg component to render the SVG. I followed [this post](https://stackoverflow.com/questions/28568794/amazon-s3-javascript-no-access-control-allow-origin-header-is-present-on-the) and opened up my AWS S3 CORS. I'll experiment with removing headers to see what the optimal configuration is. – andrewhl Sep 09 '17 at 14:45

1 Answers1

2

The answer was to enable CORS in the AWS S3 console. See stackoverflow.com/questions/17533888/s3-access-control-allow-origin-header. The CORS configuration does not appear to be enabled by default, so I had to save it for it to take effect.

<!-- Sample policy -->
<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Thank you to sideshowbarker for this recommendation. As he mentioned, if you are serving images from another domain, which you do not have access to, you may need to use a proxy. See stackoverflow.com/a/43881141/441757, under the section How to use a CORS proxy to get around “No Access-Control-Allow-Origin header” problems, for details.

andrewhl
  • 992
  • 1
  • 11
  • 31