如何在 TypeScript remix 專案中把 .svg 引用成 React 元件
搜尋 remix SVG as react component 會找到SVGR 官方提供的 remix 設定,但感覺太複雜了。其實可以直接用 vite-plugin-svgr 來處理就好。
步驟
先安裝套件:
npm i --D vite-plugin-svgr @svgr/plugin-jsx @svgr/plugin-prettier @svgr/plugin-svgo
yarn add --D vite-plugin-svgr @svgr/plugin-jsx @svgr/plugin-prettier @svgr/plugin-svgo
然後在專案根目錄新增 svgr.config.cjs
與 svgo.config.cjs
。使用 .cjs
是因為用 .js
設定檔沒辦法讓 npm run remix vite:dev
啟動專案。錯誤訊息如下:
[vite] Internal server error: require() of ES Module (略)/svgo.config.js from (略)/node_modules/cosmiconfig/dist/loaders.js not supported.
Instead change the require of svgo.config.js in (略)/node_modules/cosmiconfig/dist/loaders.js to a dynamic import() which is available in all CommonJS modules.
接著在 svgr.config.cjs
中設定要引用的外掛(plugin):
module.exports = {
plugins: ["@svgr/plugin-svgo", "@svgr/plugin-jsx", "@svgr/plugin-prettier"],
};
不需要加上 typescript: true
,因為轉成 .tsx
反而會讓 vite 無法順利解析。錯誤訊息大概長這樣:
7:34:05 PM [vite] Internal server error: Transform failed with 1 error:
(略)/app/assets/icons/arrow_back.svg?react:2:12: ERROR: Expected "from" but found "{"
Expected "from" but found "{"
1 | import * as React from 'react';
2 | import type { SVGProps } from 'react';
| ^
3 | const SvgArrowBack = (props: SVGProps<SVGSVGElement>) => (
4 | <svg xmlns="http://www.w3.org/2000/svg" width={24} height={24} viewBox="0 0 24 24" {...props}>
然後在 svgo.config.cjs
設定 .svg 的加工需求:
module.exports = {
plugins: [
{
name: "removeAttrs",
params: {
attrs: "(fill|fill-rule|style)",
},
},
],
};
選擇移除 fill
和 style
是因為我會透過 tailwind css 來控制 .svg (圖示)的顏色。
再把設定檔與 vite-plugin-svgr
加進 vite.config.js
:
import svgr from "vite-plugin-svgr";
import svgrOptions from './svgr.config.cjs';
export default {
// ...
plugins: [svgr({ svgrOptions }),],
};
快完成了。在 ./app
資料夾內建立 custom.d.ts
。檔名中的 custom
可以換成其他名稱。在 custom.d.ts
加入以下內容:
declare module "*.svg" {
const content: string;
export default content;
}
declare module "*.svg?react" {
import * as React from "react";
export const ReactComponent: React.FunctionComponent<
React.ComponentProps<"svg">
>;
export default ReactComponent;
}
這代表這個專案的 .svg 檔有兩種用法。一個是在路徑尾巴加上 ?react
把 .svg 當成 React 元件用。一個是不加上 ?react
來載入 .svg 的檔案路徑。參考以下範例:
import ArrowBack from "~/assets/icons/arrow_back.svg?react";
import arrowBackSrc from "~/assets/icons/arrow_back.svg";
function IconButton() {
return (
<button>
<ArrowBack className="block h-6 w-6" />
<img src={arrowBackSrc} alt="" />
</button>
);
}
最後,把 custom.d.ts
加進 tsconfig.json
的 compilerOptions.types
中:
{
"compilerOptions": {
"types": ["@remix-run/node", "vite/client", "./app/custom.d.ts"]
}
}
完成 👏