Can't load Expo Vector Icons in Nextjs

Viewed 200

I have tried a lot of different ways to do this, with absolutely zero luck, over multiple days.

I am trying to use Solito Nativebase Universal Typescript repo to do this:

https://github.com/GeekyAnts/nativebase-templates/tree/master/solito-universal-app-template-nativebase-typescript

I have read, and tried everything on this page at least a dozen times:

https://github.com/GeekyAnts/nativebase-templates/issues/43

My current next.config.js file looks like this:

/** @type {import('next').NextConfig} */

const { withNativebase } = require('@native-base/next-adapter')
const withImages = require('next-images')
const { withExpo } = require('@expo/next-adapter')
const withFonts = require('next-fonts')

module.exports = withNativebase({
  dependencies: [
    '@expo/next-adapter',
    'next-images',
    'react-native-vector-icons',
    'react-native-vector-icons-for-web',
    'solito',
    'app',
  ],
  plugins: [
    [withFonts, { projectRoot: __dirname }],
    withImages,
    [withExpo, { projectRoot: __dirname }],
  ],
  nextConfig: {
    images: {
      disableStaticImages: true,
    },
    projectRoot: __dirname,
    reactStrictMode: true,
    webpack5: true,
    webpack: (config, options) => {
      config.resolve.alias = {
        ...(config.resolve.alias || {}),
        'react-native$': 'react-native-web',
        '@expo/vector-icons': 'react-native-vector-icons',
      }
      config.resolve.extensions = [
        '.web.js',
        '.web.ts',
        '.web.tsx',
        ...config.resolve.extensions,
      ]
      return config
    },
  },
})

I have also tried using @native-base/icons, again, no luck.

My end use case is this:

export const Cart = (props: IIconStyles) => {
  return (
    <Icon
      as={FontAwesome5}
      name="shopping-cart"
      size={props.size ? props.size : 6}
      color="gray.200"
    />
  )

Theoretically it SHOULD show a shopping cart, but instead, this is what I see:

question mark icon

So clearly there's some font issue or other issue that is preventing it from loading in the actual SVG.

I can't figure out what this is - I've tried rewriting my _document.tsx file like this:

https://docs.nativebase.io/nb-icons

I've tried adding this to my next.config.js:

 config.module.rules.push({
   test: /\.ttf$/,
   loader: "url-loader", // or directly file-loader
   include: path.resolve(__dirname, "node_modules/@native-base/icons"),
 });

When I try to do something like this:

import fontsCSS from '@native-base/icons/FontsCSS';

in my _document.tsx file, I get the following error:

Module not found: Can't resolve '@native-base/icons/lib/FontsCSS'

Despite the fact that I've got @native-base/icons installed in my package.json, as well as having it in my Babel file per the instruction link above.

How do I get vector icons to work in Next?

Note, this is specifically Next/Expo/React Native

2 Answers

You can read more about setup of next-adapter-icons here.

I got it working with following approach,

  • next.config.js
const { withNativebase } = require("@native-base/next-adapter");
const path = require("path");

module.exports = withNativebase({
  dependencies: ["@native-base/icons", "react-native-web-linear-gradient"],
  nextConfig: {
    webpack: (config, options) => {
      config.module.rules.push({
        test: /\.ttf$/,
        loader: "url-loader", // or directly file-loader
        include: path.resolve(__dirname, "node_modules/@native-base/icons"),
      });
      config.resolve.alias = {
        ...(config.resolve.alias || {}),
        "react-native$": "react-native-web",
        "react-native-linear-gradient": "react-native-web-linear-gradient",
        "@expo/vector-icons": "react-native-vector-icons",
      };
      config.resolve.extensions = [
        ".web.js",
        ".web.ts",
        ".web.tsx",
        ...config.resolve.extensions,
      ];
      return config;
    },
  },
});
  • pages/_document.js
import React from 'react';
import { DocumentContext, DocumentInitialProps } from 'next/document';
import { default as NativebaseDocument } from '@native-base/next-adapter/document'

// Icon Font Library Imports
import MaterialIconsFont from '@native-base/icons/FontsCSS/MaterialIconsFontFaceCSS';
import EntypoFontFaceCSS from '@native-base/icons/FontsCSS/EntypoFontFaceCSS';
const fontsCSS = `${MaterialIconsFont} ${EntypoFontFaceCSS}`;

export default class Document extends NativebaseDocument {

  static async getInitialProps(ctx) {
    const props = await super.getInitialProps(ctx);
    const styles = [
      <style key={'fontsCSS'} dangerouslySetInnerHTML={{ __html: fontsCSS }} />,
      ...props.styles,
    ]
    return { ...props, styles: React.Children.toArray(styles) }
  }
}
  • pages/index.tsx
import React from "react";
import { Box, Icon } from "native-base";
import Entypo from "@expo/vector-icons/Entypo";

export default function App() {
  return (
    <Box>
      <Icon
        as={Entypo}
        name="user"
        color="coolGray.800"
        _dark={{
          color: "warmGray.50",
        }}
      />
    </Box>
  );
}

Using import like this:

import MaterialIcons from '@expo/vector-icons/MaterialIcons'

in place of:

import { MaterialIcons } from '@expo/vector-icons'

worked for me. I think this is because of the way babel/webpack handles imports in the template. I followed the steps here to setup the icons.

Here's what that looks like on web: screenshot

Related