ysjn
ysjn

Reputation: 468

Getting undefined when importing svg as ReactComponent in create-react-app project

I'm getting following error when trying to load svg as ReactComponent.

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of Icon.

I'm using create-react-app with typescript option to create project. If I'm understanding the document correctly, I should be able to import svgs as ReactComponent out of the box(since CRA2.0). But weirdly, I'm getting undefined for the imported component.

Adding Images, Fonts, and Files · Create React App https://facebook.github.io/create-react-app/docs/adding-images-fonts-and-files#adding-svgs

Currently using following packages.

    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-redux": "^6.0.1",
    "react-router-dom": "^5.0.1",
    "react-scripts": "^3.0.1",
    "@types/node": "^12.0.0",
    "@types/react": "^16.8.17",
    "@types/react-dom": "^16.8.4",
    "@types/react-redux": "^7.0.8",
    "@types/react-router-dom": "^4.3.3",

My code is below.

Icon.tsx

import * as React from 'react';
import { ReactComponent as IconSvg } from './IconSvg.svg';

const Icon: React.FC<IconProps> = props => {
  console.log(IconSvg); // undefined
  return (<IconSvg />);
};

export default Icon;

custom.d.ts

declare module '*.svg' {
  import React = require('react');
  export const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
  const src: string;
  export default src;
} 

tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "baseUrl": "src",
    "rootDir": "src",
    "outDir": "build/dist",
    "target": "es5",
    "jsx": "react",
    "lib": [
      "es6",
      "dom"
    ],
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": false,
    "allowSyntheticDefaultImports": true
  },
  "include": [
    "src",
    "custom.d.ts"
  ],
  "exclude": [
    "node_modules",
    "node_scripts",
    "build",
    "scripts",
    "acceptance-tests",
    "webpack",
    "jest",
    "src/setupTests.ts"
  ]
}

Any advice is appreciated.

Upvotes: 23

Views: 9214

Answers (5)

Nader Zouaoui
Nader Zouaoui

Reputation: 43

if what you need is to import SVGs as a component in React I would recommend this tool https://react-svgr.com/playground/ else the simplest way to deal with SVGs in react is the one mentioned by the other answers above.

Upvotes: 0

Gabriel Petersson
Gabriel Petersson

Reputation: 10422

TLDR: import MySvg from "MySvg.svg" then <img src={MySvg}/>

To import an SVG: import MySvg from "MySvg.svg" which will save the path as a string inside MySvg.

To render it, use an img tag as in standard HTML, since MySvg is actually a path: <img src={MySvg}/>

The reason you get undefined is that you are destructuring a string.

Upvotes: 3

Nathan Scott
Nathan Scott

Reputation: 11

Another example of how to do SVG's with Typescript:-


export interface ISVG {
    viewBox?: string;
    width?: string;
    height?: string;
    fill?: string;
    className?: string;
}

export const SVG = ({
    viewBox = '0 0 200 200',
    fill = '#FA4D56',
    width,
}: ISVG): JSX.Element => {
    return (
        <svg viewBox={viewBox} width={width}>
            <path
                fill={fill}
                d="M41.9,-43.6C54,-39.8,63.3,-26.3,59.5,-15.3C55.8,-4.2,39,4.3,31.4,18.6C23.7,32.8,25.1,52.8,16,65.4C6.9,78,-12.7,83.2,-25.5,76C-38.4,68.8,-44.5,49,-44.5,33.3C-44.5,17.6,-38.4,6,-37.3,-7.8C-36.3,-21.5,-40.5,-37.4,-35.1,-42.3C-29.8,-47.2,-14.9,-41.3,0,-41.3C14.9,-41.3,29.8,-47.3,41.9,-43.6Z"
                transform="translate(100 100)"
            />
        </svg>
    );
};

You can also export the SVG as follows: -

export {SVG as Shape}; or w/e naming you choose to export it as.

Things such as margin or padding should be applied afterward, I would assume since you want them to be as generic as possible and be able to re-use them in different locations.

Upvotes: 0

Codex
Codex

Reputation: 1281

Try to use Styled components

import Logo from 'assets/logo.svg';

const Image = styled.img<Props>`<some setting for standard width and height for logo here`>

const LogoWrapper = styled(Image)`
transition: all 0.25s linear;
&: hover {
    transform: scale(1.25);
}`;

<LogoWrapper height={140} src={Logo} alt="Some Logo" />

This one is working er

Upvotes: 0

Mohammad
Mohammad

Reputation: 127

use this way this is an exaple find your svg and put it in this function

your svg file

interface IProps {
  fill?: string;
}
export default (props: IProps) => (
  <svg
    style={{ marginTop: "13px" }}
    fill={props.fill || Primary}
    viewBox="0 0 424 424"
    width={15}
  >
    <path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" />
    <path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" />
    <path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" />
  </svg>
);

where you want to use svg

import React from "react";
import Menu from "./assets/svg/menu";

 ...
 return (
   ...
   <Menu/> 
 )

Upvotes: -1

Related Questions