@nuxt/iconで快適なアイコンのレンダリングを実現する

2024/12/22に公開

こんにちは、 ryoです。
こちらはNuxt / UnJS Advent Calendar 2024の22日目の記事です。

皆さんは何らかのアイコンを表示する際、どのように実装していますか?今もimgタグを利用していますか?
ですが、imgタグだと

  • 色が変更できない
  • 大きくすると粗くなる
  • HTTPレスポンスのサイズが大きくなる
  • 相対パスの指定が面倒
  • SSRができない

というような課題にぶち当たることがよくあります。(ありますよね?)
アイコンはSVGを利用しましょう。そして、@nuxt/iconならSVGをより簡単に快適に扱うことができます。

@nuxt/icon とは?

@nuxt/iconはNuxt Modulesの1つで、導入することでIconifyが提供する200,000個以上のアイコンをすぐに利用できるようになります。
制作者は Nuxt, Vite, Vue のコアチームメンバーである Anthony Fu 氏です。

セットアップ

Nuxtのプロジェクトに@nuxt/iconを導入するのはとても簡単で、以下の2ステップです。

npmインストール

npm i -D @nuxt/icon

configに指定

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/icon'
  ]
})

以上で、利用する準備が整いました。早速、SVGを表示してみましょう。

Iconifyを使う

IconifyからVue.jsを利用してみます。
実装はとても簡単で、Iconタグを用います。

Iconifyを利用する
<Icon name="uil:vuejs" style="color: blue" size="2em" />
<Icon name="uil:vuejs" style="color: pink" size="2.5em" />
<Icon name="uil:vuejs" style="color: red" size="3em" />
<Icon name="uil:vuejs" style="color: black" size="3.5em" />
<Icon name="uil:vuejs" style="color: green" size="4em" />

  • name : Iconify上でのアイコン名を指定する
  • style : CSSで色の見栄えの編集が可能になる
  • size : 任意のサイズに変更できる(デフォルトは1em)

mode について

spanタグとsvgタグのどちらでレンダリングするかをmodeで切り替えられます。

modeにcssを指定
<Icon name="uil:vuejs" style="color: blue" size="2em" mode="css" />
spanタグでレンダリングされる
<span class="iconify i-uil:vuejs icon" aria-hidden="true" style="font-size:2em;color:blue;" data-v-938b83b0=""></span>

modeにsvgを指定
<Icon name="uil:vuejs" style="color: blue" size="2em" mode="svg" />
svgタグでレンダリングされる
<svg data-v-938b83b0="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="icon iconify iconify--uil" width="1em" height="1em" viewBox="0 0 24 24" style="font-size: 2em; color: blue;"><path fill="currentColor" d="M18.03 2.443h-3.643L12.013 6.4L9.63 2.444l-2.646-.001H.831L12.03 21.558L23.168 2.443Zm-6.005 15.15L4.322 4.443h2.824l4.885 8.406l4.847-8.407h2.81Z"></path></svg>

Vueコンポーネントを使う

components/global/ フォルダ配下に置いたVueコンポーネントを利用して、SVGを表示することもできます。

components/global/Nuxt.vue
<template>
  <svg xmlns="http://www.w3.org/2000/svg" width="1.53em" height="1em" viewBox="0 0 256 168"><path fill="#00DC82" d="M143.618 167.029h95.166c3.023 0 5.992-.771 8.61-2.237a16.96 16.96 0 0 0 6.302-6.115a16.3 16.3 0 0 0 2.304-8.352c0-2.932-.799-5.811-2.312-8.35L189.778 34.6a16.97 16.97 0 0 0-6.301-6.113a17.6 17.6 0 0 0-8.608-2.238c-3.023 0-5.991.772-8.609 2.238a16.96 16.96 0 0 0-6.3 6.113l-16.342 27.473l-31.95-53.724a17 17 0 0 0-6.304-6.112A17.64 17.64 0 0 0 96.754 0c-3.022 0-5.992.772-8.61 2.237a17 17 0 0 0-6.303 6.112L2.31 141.975a16.3 16.3 0 0 0-2.31 8.35c0 2.932.793 5.813 2.304 8.352a16.96 16.96 0 0 0 6.302 6.115a17.6 17.6 0 0 0 8.61 2.237h59.737c23.669 0 41.123-10.084 53.134-29.758l29.159-48.983l15.618-26.215l46.874 78.742h-62.492zm-67.64-26.24l-41.688-.01L96.782 35.796l31.181 52.492l-20.877 35.084c-7.976 12.765-17.037 17.416-31.107 17.416"></path></svg>
</template>

<script lang="ts">
export default {
  name: 'Nuxt'
}
</script>
Vueコンポーネントを利用する
<Icon name="Nuxt" size="4em" />

Vueコンポーネントとnameを合わせておく必要があります。
また、常に結果はsvgタグでレンダリングされます。(modeは無視される。)

オリジナルのSVGファイルを使う

自社サイトのアイコンや自作したアイコンなどを表示したい場合もあります。
その場合、カスタムコレクションという機能を利用して、SVGを表示することができます。

例として、以下のsvgファイルを表示させたいとします。

vite.svg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

まずは、任意のフォルダにsvgファイルを格納します。
ここでは ./assets/my-icons フォルダ配下とします。

assets/my-icons
├── vite.svg

次に nuxt.config.tsicon.customCollections の欄を追加します。

nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/icon'
  ],
  icon: {
    customCollections: [
      {
        prefix: 'my-icon',
        dir: './assets/my-icons'
      },
    ],
  },
})

これで準備が整ったので、vite.svgを表示してみましょう。

vite.svgを利用する
<Icon name="my-icon:vite" size="4em" />

アイコンが表示されました!

初期値を設定する

app.config.ts にデフォルトの値を指定することができます。

app.config.ts
export default defineAppConfig({
  icon: {
    size: '4em',
    class: 'icon',
    mode: 'css',
    aliases: {
      'vitest': 'logos:vitest'
    }
  }
})

Iconタグでsizeやmodeを指定しなければ、 app.config.ts に記載した値が設定されます。
また、aliasesを利用すれば指定したSVGを表示できるようになります。

aliasesを利用する
<Icon name="vitest" />

おわりに

現代のフロントエンドは本当に複雑化していて、アイコン1つ取っても、スケーラビリティ・動的ローディング・SSRなど考慮しなければならない課題が複数あります。
それらの課題に対して最適なアプローチが取れるように、 @nuxt/icon を活用していきたいと思います。

参考

先日の Nuxt Nation 2024 で @nuxt/icon が紹介されていました。
https://www.youtube.com/watch?v=RKknKGWMgzI&list=PLxddmVXxb3HtMg8t3WOYTizYbFSO_B_ST&index=22

Vue・Nuxt 情報が集まる広場 / Plaza for Vue・Nuxt.

Discussion