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:
img
element with url is not good as I need to change svg properties.public
folder are not good in my
case as I'd like to maintain the standard public folder as the folder
for assets in order to receive built in supports from different
libraries.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!