Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't use is="NuxtLink" with component #13659

Closed
AxelBriche opened this issue Apr 5, 2022 · 15 comments
Closed

Can't use is="NuxtLink" with component #13659

AxelBriche opened this issue Apr 5, 2022 · 15 comments

Comments

@AxelBriche
Copy link

AxelBriche commented Apr 5, 2022

Environment


  • Operating System: Darwin
  • Node Version: v14.18.2
  • Nuxt Version: 3.0.0-27468803.23e7afb
  • Package Manager: [email protected]
  • Builder: vite
  • User Config: css, build
  • Runtime Modules: -
  • Build Modules: -

Reproduction

<component is="NuxtLink" to="/">Example</component>

Generate this html:

<nuxtlink to="/">Example</nuxtlink>

Expected html:

<a aria-current="page" href="/" class="router-link-active router-link-exact-active">Example</a>

Describe the bug

Usage of <component is="NuxtLink" to="/"> doesn't generate a link, I can't use <component :is="to ? 'NuxtLink' : href ? 'a' : 'button'"

Additional context

No response

Logs

No response

@lukaszflorczak
Copy link

lukaszflorczak commented Apr 5, 2022

First you can use nuxt-link for external links now. Check the doc: https://v3.nuxtjs.org/docs/usage/nuxt-link.

If you still need to use it in is prop, you can use defineNuxtLink({}) instead of nuxt-link:

const component = computed(() => {
  if (props.to || props.href) return defineNuxtLink({})
  return 'button'
})

@danielroe
Copy link
Member

Or, to reuse the existing NuxtLink component:

const component = computed(() => {
  if (props.to || props.href) return resolveComponent('NuxtLink')
  return 'button'
})

Example: https://stackblitz.com/edit/github-vwzpk9?file=app.vue.

The reason this is required is that if you don't use NuxtLink it won't be included in your bundle. So we need more than just a string to tell us.

If you want it globally registered then you can create your own NuxtLink component with defineNuxtLink in your components directory and set components.global to true in your nuxt.config. See nuxt/framework#3305 for some context.

@danielroe danielroe closed this as not planned Won't fix, can't repro, duplicate, stale Apr 6, 2022
@danielroe danielroe added the 3.x label Jan 19, 2023
@danielroe danielroe transferred this issue from nuxt/framework Jan 19, 2023
@iamandrewluca
Copy link
Contributor

Does resolveComponent work only in an SFC context? 🤔

I tried it in a *.vue file, it returns the actual component
I tried in a *.ts file, it returns NuxtLink string

@Dodje
Copy link

Dodje commented Jun 2, 2023

There's kinda strange behavior when I try to resolveComponent inside of ui-library. It cant resolve NuxtLink component

I have a nuxt page where I use a link component:

<template>
    <base-link as="nuxt-link">Link</base-link>
 </template>
 
 <script setup>
import BaseLink from '@private-library/ui';
 </script>

The components looks like

<template>
    <component is="linkComponent" ...>
    ...
</template>
<script ...>
...
const linkComponent = resolveComponent('NuxtLink');
// console.log(linkConponent); -> null
</script>  

Copy link
Member

@Dodje 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'...

@Dodje
Copy link

Dodje commented Jun 2, 2023

@danielroe thanks for a quick response
Due your suggestion I was able to create ConcreteComponent with import { defineNuxtLink } from '#imports' (or just import NuxtLink from #components, and it seems to work fine on server side, but i'm getting error "Failed to resolve component: NuxtLink" on the client side after hydration

Copy link
Member

I'll happily look at a reproduction.

@Dodje
Copy link

Dodje commented Jun 2, 2023

Sometimes life is like a joke, I've created a reproduction repo and... it works just fine :)
Tried again in the project, and it works too..
So, thanks again 👍

@danielroe
Copy link
Member

Excellent. 👌

@mrleblanc101
Copy link

@danielroe import { NuxtLink } from '#imports' does not work for me.

Internal server error: [unimport] failed to find "NuxtLink" imported from "#imports"

@mrleblanc101
Copy link

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.

@rstainsby
Copy link

rstainsby commented Sep 13, 2023

I'm having issues with this when generating a NuxtLink using a dynamic component in an external library.

I'm trying to support both Vue and Nuxt projects, so I have a component that detects the environment and renders the correct component (see below).

// How I'm detecting the environment/resolving the component
function computeTag(to: string | undefined) {
  const instance = getCurrentInstance();

  if (!instance || !instance.proxy) {
    throw new Error('No instance of Vue detected');
  }  

  const hasNuxt = instance.proxy && '$nuxt' in instance.proxy;
  const hasRouter = instance.proxy && '$router' in instance.proxy;

  if (!hasRouter || (hasRouter && !to)) {
    return 'a';
  }

  return hasNuxt ? resolveComponent('NuxtLink') : resolveComponent('router-link');
}

// The component template
<component
      :is="computedTag"
      v-bind="computedProps"
    >
      <i v-if="icon" :class="icon"></i>

      <span class="font-light">
        <slot></slot>
      </span>

      <i v-if="isExternal" class="ml-auto pi pi-external-link" style="font-size: 0.75rem;"></i>
      <Badge v-else-if="badge" class="ml-auto" :class="badgeClass" :value="badge"> </Badge>
    </component>

The works for Vue projects but on Nuxt projects it is unable to resolve the NuxtLink component. Is there something I'm missing or is this just not possible with Nuxt?

I should also add that this library is written using just Vue, not Nuxt, due to some other restrictions.

------EDIT --------

After spinning my wheels on this for a few hours I fixed the issue within 2 minutes of posting this, typical.

It seems like the Vue instance isn't actually aware of the NuxtLink when it renders my component, I'm guessing has something to do with Nuxt's auto-importing. So, as part of my library import I added this to my plugin definition:

// ./plugins/my-ui-library.ts
import { defineNuxtPlugin } from "#app";
import { NuxtLink } from "#components";

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.component("NuxtLink", NuxtLink);
});

Can someone confirm if this is the right way to resolve this issue and maybe give a bit of clarification as to why it works?

Copy link
Member

Yes, that's a very reasonable solution. The only reason we don't register NuxtLink globally as if you don't use it we can tree-shake it out of your build.

@deflexor
Copy link

deflexor commented Oct 3, 2023

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.

This doesnt seem to work, getting this all the time:

Property "NuxtLink" was accessed during render but is not defined on instance

@danielroe
Copy link
Member

@deflexor it will work if you use a script setup block, where the import is automatically exposed. Otherwise you need to pass it explicitly to the template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants