ことの発端
以前、Next.jsでSVGをコンポーネントのように扱う方法を調べました。
今日もまた、いつものようにbabel-plugin-inline-react-svgを使ってSVGを読み込もうとしたのですが、
Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
と、エラーが出ました。
調査
importしたSVGをreturnするのを一旦諦め、
import Logo from '../../images/Logo.svg'; console.log(Logo);
という形でimportしたものを確認すると、
{ height: 80, src: "/_next/static/image/src/images/Logo.xxxxxxxxxxxxxxxx.svg", width: 80 }
という感じのオブジェクトが返ってきていることがわかりました。
これではコンポーネントのようには扱えません。
一方、表示が成功しているプロジェクトのSVGをconsole.logで表示すると、
Logo(props) { return /*#__PURE__*/(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_2__.jsxDEV)("svg", _objectSpread(_objectSpread({}, props), {}, { children: /*#__PURE__*/(0,react_jsx_dev_ru…
と、途中で切れてしまっていますが、関数が返ってきていることがわかります。
原因
オブジェクトが返ってくるケースと、関数が返ってくるケースで何が違うのか。
調査を進めると、
yarn create next-app --typescript
で作成するとオブジェクトが、
yarn create next-app
で作成したあと、自力でTypeScriptを導入すると関数が返ってくることがわかりました。
これが一時的なものなのか、そういう仕様なのかはわかりません。
おそらく、内部のwebpackの設定が微妙に違うんだと思われます。
対策 @svgr/webpackに乗り換える
yarn create next-app --typescript
にて作成したプロジェクトにbabel-plugin-inline-react-svgを導入するときはどうすれば良いのか。
いろいろ試した結果、
@svgr/webpackを使い、SVGを読み込ませればエラーなく読み込むことができました。
導入手順としては、
❶ @svgr/webpackを導入する
yarn add -D @svgr/webpack
❷ next.config.jsを編集する
module.exports = { webpack(config) { config.module.rules.push({ test: /\.svg$/, issuer: { and: [/\.(js|ts)x?$/] }, use: ['@svgr/webpack'] }); return config; } };
で、OKです。
@svgr/webpackを導入すれば、babel-plugin-inline-react-svgは削除してしまって大丈夫でした。