reactjssvgvitesvgr

How to import svg files in public directory with react vite svgr


How to use SVGR with React's 'public' folder assets?

I'm using react + vite + svgr and my svg files are located in the "public" directory. My project structure is

|
|- src
|- public
    |- icons
        |- svgs
            |- mySvg.svg
|- vite.config.ts
|- package.json

since public is outside of the src folder I get the error:

Assets in public directory cannot be imported from JavaScript.

If you intend to import that asset, put the file in the src directory, and use /src/assets/icons/svgs/mySvg.svg instead of /public/assets/icons/svgs/mySvg.svg.

Clarification:


Solution

  • In the end, I couldn't find / get a proper solution, so I'll post my work around as the solution for future developers (in hopes someone can supply a better one or create a better one from my solution):

    SVGR has CLI support features that can be used in order to create a script that will generate react component for each *.svg file in the public folder

    In your package.json file add the "update-icons" script:

    "scripts":
        "start": ...,
        ...
        "update-icons": "chmod u+x scripts/icons/update-icons.sh && scripts/icons/update-icons.sh", <--- add this line
        ...
    ...
    

    create template files for your generated components and index files at a folder to your liking (e.g. "src/assets/templates"):

    // example for component template in file named "componentTemplate.ts"
    
    import { Template } from "@svgr/babel-plugin-transform-svg-component";
    
    export const defaultTemplate: Template = (variables, { tpl }) => {
        return tpl`
    import type { SVGProps, ReactElement } from "react";
    
    ${variables.interfaces};
    
    export default function ${variables.componentName}(${variables.props}): ReactElement {
      return (
          ${variables.jsx}
      );
    };
    
    `;
    };
    
    export default defaultTemplate;
    
    
    // example for index template in file named indexTemplate.ts
    
    export const formatExportName = (name: string): string => {
        if (/[-]/g.test(name) && /^\d/.test(name)) {
            return `Svg${pascalCase(name)}`;
        }
    
        if (/^\d/.test(name)) {
            return `Svg${name}`;
        }
    
        return pascalCase(name);
    };
    
    const defaultIndexTemplate: IndexTemplate = (paths: FileInfo[]) => {
        const exportEntries = paths.map(({ path: filePath }) => {
            const basename = path.basename(filePath, path.extname(filePath));
            const exportName = formatExportName(basename);
    
            return `export { default as Svg${exportName} } from './${basename}'`;
    });
    
        return exportEntries.join("\n");
    };
    

    create a shell script file named, for example "update-icons.sh", under some dedicated folder (e.g. "scripts/icons/").

    In the file content add the following

    #!/bin/bash
    
    # configure below variables according to your project specific needs
    baseFolder=$(pwd)
    dest=$baseFolder/src/assets/generated
    src=$baseFolder/public/assets
    templatesPath=src/assets/templates # you template files
    templates=( componentTemplate indexTemplate )
    folders=( svgs ) # you can specify a list of folders here instead of generating all files in the public folder
    BLUE="\033[0;34m" # this is used to make the console output prettier
    NC="\033[0m" # this is used to make the console output prettier
    
    rm -r "$dest" # remove existing files from the destination folder that contains the generated svg React components
    
    # my project uses ESNext so I need to first transpile the related template files to CommonJS (for SVGR)
    echo -e "${BLUE}Transpiling templates${NC}"
    for template in "${templates[@]}"
    do
      npx tsc --module commonjs --skipLibCheck true "./$templatesPath/$template.ts"
      cp "$baseFolder/$templatesPath/$template.js" "$baseFolder/$templatesPath/$template.cjs"
      rm "$baseFolder/$templatesPath/$template.js"
    done
    echo "Done"
    echo ""
    
    # this is the main part of the solution
    echo -e "${BLUE}Generating React components${NC}"
    for folder in "${folders[@]}"
    do
          npx @svgr/cli --typescript --filename-case kebab --no-dimensions --index-template "./${templatesPath}/indexTemplate.cjs" --template "./${templatesPath}/componentTemplate.cjs" --out-dir "$dest/${folder}" "$src/${folder}"
    done
    
    # remove redundant CommonJS files
    for template in "${templates[@]}"
    do
      rm "$baseFolder/$templatesPath/$template.cjs"
    done
    

    Now just run "npm run update-icons" Hope this helps!