I have a file with icons being exported from images.ts
:
export { default as SvgArrow } from './arrow.svg';
export { default as SvgCopy } from './copy.svg';
...
export { default as SvgWater } from './water.svg';
That’s a part of a design system library.
Then my Icon component imports the icons:
import * as Icons from '../images';
and within render sets the current icon:
const CurrentIcon = Icons[iconName];
With such implementation all the components in a library using <Icon />
had all the svgs in the build bundle. Because of that the bundle was very big – around 6mb.
I implemented the dynamic loading of the current icon.
const [IconComponent, setIconComponent] = useState<React.ElementType | null>(
null,
);
useEffect(() => {
const loadIcon = async () => {
const icons = await import('../icons');
setIconComponent(() => icons[iconName]);
};
loadIcon();
}, [iconName]);
The thing is that, although it worked in a way that bundle is no 700kB instead of 6mb, the TypeScript fails – meaning that in my consuming app I’m getting tons of errors, mostly like :
You may need an appropriate loader to handle this file type, currently
no loaders are configured to process this file. See
https://webpack.js.org/concepts#loaders
The dynamic loading was the only change. Right when I revert it all works in my app, but in the same time the build is again at 6mb.
I’ve tried many solutions – webpack magic comments, JS eval (which worked, but that’s not the right approach), better typings for the useEffect. None of them worked, still same TS errors.
My tsconfig is:
{
"compilerOptions": {
"baseUrl": ".",
"target": "es2020",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"declaration": true,
"outDir": "./dist",
"rootDir": ".",
"declarationDir": "./dist",
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true,
"module": "es2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"sourceMap": true,
"declarationMap": true,
"jsx": "react-jsx",
"incremental": false,
"paths": {
"components/*": ["components/*"],
"utils/*": ["utils/*"],
},
"plugins": []
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules", "dist"]
}
I tried all the solutions for this question, but non of them worked – apart from JS eval which is generally considered a bad practice for security and performance reasons.
I’m also using @svgr/webpack
in webpack, without dynamic loading my package for the design system works without any issues, also icons.
Even now, with all the TS errors, the consuming app works if I do:
{
test: /.d.ts$/,
loader: 'ignore-loader',
},
but that’s not what I want. I want my design system package to work out of the box without any TS issues and with the dynamic import of the icons.