How to Use SVG with React and ReasonML?

reasonreact

#1

With create-react-app and JavaScript/TypeScript, I understand I’m able to “import” an SVG as noted below. How may I do so with ReasonML in a create-react-app code base?

import { ReactComponent as Logo } from './logo.svg';

function App() {
  return (
    <div>
      {/* Logo is an actual React component */}
      <Logo />
    </div>
  );
}

#2

As far as I know, there’s (currently) no way to output such named ES6 import with BuckleScript.

I’ve worked around this particular create-react-app-SVG issue by re-exporting the Logo component from a JavaScript file and importing it in Reason:

/* logo.js */
import { ReactComponent } from './logo.svg';
export default ReactComponent;
/* App.re */
module Logo = {
  [@react.component] [@bs.module "./logo.js"]
  external make: unit => React.element = "default";
};

[@react.component]
let make = () => {
  <div>
    {<Logo /> /* Logo is an actual React component */}
  </div>
}

#3

Thanks for sharing your workaround. While it definitely works, I’m curious to know whether you were able to pass props to the original JS component. I haven’t figured out how to do so; consequently, I apply props such as “className” to the JS component as opposed to passing it through the Reason based component.


#4

I’m not sure why the named import would be a problem for BuckleScript. Does this not work?

module Logo = {
  [@react.component] [@bs.module "./logo.svg"]
  external make: unit => React.element = "ReactComponent";
};

[@react.component]
let make = () => {
  <div>
    <Logo />
  </div>
}

#5

Named import as suggested below results in the following error:
Cannot read property 'name' of undefined at Array.map (<anonymous>)

Any ideas as to why that is or how to resolve it? Thanks.


#6

I fiddled around with this a bit, and it looks like CRA does some compile-time magic to the JavaScript. It will either import "./logo.svg" as either a string or an object containing a React component depending on how the import statement is written. It looks like there’s a GitHub issue about it here.

This imports a React component:

import { ReactComponent as Logo } from './logo.svg';

This imports a string:

import logo from './logo.svg';

And this is an error:

import * as LogoSvg from "./logo.svg";
var make = LogoSvg.ReactComponent;

The latter is what BuckleScript compiles to. Even though it’s perfectly valid JavaScript (and should be equivalent to the first statement), CRA doesn’t compile it as expected.

@pewniak747’s workaround is one solution. You can also use %bs.raw to achieve the same effect:

%bs.raw
{|import {ReactComponent as _Logo} from "./logo.svg"|};

module Logo = {
  [@react.component] [@bs.val] external make: unit => React.element = "_Logo";
};

#7

reason-react documentation has a section about binding to JavaScript components with props: https://reasonml.github.io/reason-react/docs/en/components#import-from-js