The react-native-vector-icons (rnvi
) is a popular library in the react-native world, which is compatible with react-native-web.
But there is a problem. When used with react-native-web, rnvi
significantly increases the bundle size. Each ttf
font has a corresponding GlyphMap, which is a json
file that associates the icon's name with its Unicode value. Even if you use only one icon in your project, the entire GhyphMap and the entire ttf
will be imported by esbuild during the bundling. Some GlyphMaps, such as Ionicons and MaterialCommunityIcons, are over 100 kb, and minimization has almost no effect. And the MaterialCommunityIcons.ttf
, which is more than 900 kb, will be download into your app.
It can get even worse, because popular libraries as react-native-paper and @react-navigation/material-bottom-tabs depend on rnvi
for the MaterialCommunityIcons font. You can opt-out rnvi
using babel-plugin-optional-require
, but, as you can infer, it doesn't work with esbuild. When you install one of these libraries in a react-native-web project, esbuild will ask you to install rnvi
and will bundle the large GlyphMap for the MaterialCommunityIcons, even if you haven't used any icons in your project.
In this post, we will discuss some options to solve this problem.
Expo
Before we discuss the options, let me give you a suggestion. Use @expo/vector-icons
instead of react-native-vector-icons
, because @expo/vector-icons
is distributed with ES modules, that allow tree-shaking and it will configure the icon font in your webpage automatically for you:
<html>
<head>
<style id="expo-generated-fonts" type="text/css">
@font-face {
font-family: material-community;
src: url(/assets/MaterialCommunityIcons-QCSDDVWU.ttf);
font-display: auto;
}
</style>
...
@expo/vector-icons
allow the use of customized fonts or subsets of fonts created with Fontello or Icomoon. Unfortunately, these sites don't work with MaterialCommunityIcons.
Behind the scenes, @expo/vector-icons
uses the library expo-font
to do it. You can use these Expo libraries even out of a Expo project.
To use @expo/vector-icons
with react-native-web and esbuild, configure your tsconfig.json/jsconfig.json
:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"react-native": ["./node_modules/react-native-web"],
"react-native-vector-icons/MaterialCommunityIcons": [
"./node_modules/@expo/vector-icons/MaterialCommunityIcons.js"
]
}
}
}
You need to specify one alias for each icon font that you use.
Solutions
Removing MaterialCommunityIcons when you are not using any Icon with react-native-paper or react-navigation
If you are using react-native-paper
or @react-navigation/material-bottom-tabs
and you are not using any icon from MaterialCommunityIcons, you can remove it with the following plugin:
require('esbuild')
.build({
...
plugins: [{
name: 'remove-materialicons',
setup(build) {
build.onLoad({ filter: /MaterialCommunityIcons.js/ }, () => ({
loader: 'jsx',
contents: `
import * as React from 'react';
import { Text } from 'react-native';
export default ({ name, color, size, ...rest }) => (
<Text {...rest}
style={{backgroundColor: 'transparent', color, fontSize: size }}
pointerEvents="none"
selectable={false}
>โก</Text>
);
`,
}))
},
},
]
})
For it to work you need to alias rnvi
to @expo/vector-icons
, according to the jsconfig.json/tsconfig.json
above.
Using material-icons-subset
The best solution is to use the package material-icons-subset
to create a subset of the MaterialCommunityIcons font.
npm i material-icons-subset
Pass a list of icon names to be included in the font as arguments in the command line:
material-icons-subset camera menu account-outline email archive
Or, you can pass the path for a config.json file:
material-icons-subset font-config.json
The config.json file must have an array called icons
with the name of the icons to be included in the font:
{
"icons": [
"camera",
"menu",
"account-outline",
"tune",
"bookmark-outline",
"pause",
"arrow-left",
"archive",
"email"]
}
The library will create two files:
materialdesignicons-webfont.ttf
and
materialdesignicons-webfont.json
with the GlyphMap.
You need to alias MaterialCommunityIcons.ttf
and MaterialCommunityIcons.json
files to the respectively new files that you created with material-icons-subset.
Unfortunately, I couldn make it work in the jsconfig.json/tsconfig.json
.
Therefore, you need to use a esbuild plugin to make it work:
const materialIconsPlugin = {
name: 'material-icons',
setup(build) {
build.onResolve({ filter: /MaterialCommunityIcons\.(ttf|json)/ }, (args) => ({
path: resolve(`./src/assets/materialdesignicons-webfont${parse(args.path).ext}`),
}))
},
}
Conclusion
That's it. Now you can use vector-icons
with react-native-web and esbuild without compromising the bundle size of your app.
Top comments (0)