Nuxt3の動的コンポーネントにて、NuxtLinkを使用する
指定の条件でNuxtLinkとspanタグの切り分けをしたいと思い、その時に試したことを共有します。
前提
- Nuxtのバージョンは3.8.1を使用しています。
- コンポーネントにて、指定のpropがある場合、NuxtLinkタグ。ない場合はspanタグを使用します。- コンポーネントのタグの切り替えをするために使用するpropsはisLinkです。
 
- コンポーネントのタグの切り替えをするために使用するpropsは
NuxtLink componentについて
Dynamic Componentについて
結論
もし対応コード・原因のみを確認したい場合は、以下のコードをご覧いただくか、GitHub のissuesを参照していただければと思います。
<!-- コンポーネントでの記載 -->
<template>
  <component :is="isLink ? NuxtLink : 'span'">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
import { NuxtLink } from '#components'
withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
</script>
<!-- 呼び出す際の処理 -->
<!-- NuxtLinkタグの場合 -->
<AnyComponent isLink />
<!-- spanの場合 -->
<AnyComponent /> <!-- or <AnyComponent isLink={false} /> -->
試してみた方法について
1. 通常のHTMLタグと同様に記載してみる
はじめとして、通常のタグと同様の感覚で、NuxtLinkを呼んでみました。
※NuxtLinkはNuxt3では auto importされるはずだと思っているため、import NuxtLinkのようなimport文についても記載はしておりません。
<template>
  <component :is="isLink ? 'NuxtLink' : 'span'">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
</script>
nuxtlinkのタグとして出力されるため、失敗

 2. resolveComponentを使用して、動的に表示させてみる
次はresolveComponentを使用し、scriptの中で動的コンポーネントとして表示させることでうまくいくか試してみようと思います。
resolveComponentの詳細については以下の記載を参照してください。
<template>
  <component :is="LinkComponent">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
const props = withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
const LinkComponent = resolveComponent(props.isLink ? 'NuxtLink' : 'span')
</script>

参考までにNuxtLinkの部分を文字列ではなくコンポーネントとして行ってみましたが、システムエラー・typeエラーになるため、こちらもうまくいきませんでした。
<template>
  <component :is="LinkComponent">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
const props = withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
const LinkComponent = resolveComponent(props.isLink ? NuxtLink : 'span')
</script>


 3. NuxtLinkをのimport文を記載してみる
ここまでで、動的コンポーネント周りの設定というよりはNuxtLink周りの設定周りが原因だと思ったため、Nuxt.jsのissuesを探してみたところ、以下のissuesを発見
issuesを見ていたところ、NuxtLinkはグローバルに登録はされていないというコメントを見つけたので、こちらを一度試してみます。
It's because NuxtLink isn't globally registered - it's auto-imported. Within a Nuxt project we detect resolveComponent('NuxtLink') and rewrite this into an import statement.
You can probably resolve this in your case by adding your ui library to build.transpile, and using import { NuxtLink } from '#imports'
<template>
  <component :is="isLink ? NuxtLink : 'span'">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
import { NuxtLink } from '#imports'
withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
</script>
こちら、#importsに以下のエラーが出力されるため、失敗

 4. NuxtLinkにcomponentsを呼んで記載してみる
さらにissuesを読み進めていくと
For those wondering, the correct import is
import { NuxtLink } from '#components';
Then you can use:<component :is="to ? NuxtLink : 'div'" :to="to">,
Nuxt need better documentation about the Virtual Files, they are helpful, but confusing.
こちら、#componentsに変更してみたところ、うまく実行させることができました。
結論の箇所にも記載していますが、改めてこちらにもコードを記載します。
※toなどの他のpropsの値については省略しています。
<template>
  <component :is="isLink ? NuxtLink : 'span'">
    何かの要素
  </component>
</template>
<script lang="ts" setup>
import { NuxtLink } from '#components'
withDefaults(
  defineProps<{ isLink?: boolean }>(),
  {
    isLink: false,
  },
)
</script>



Discussion