components: Components are not found when using is-Directive

Describe the bug I expected components to be available for use via is, like this: <component is="ComponentName">

But from the error log it seems that the components are not registered.

To Reproduce Check the console output at https://codesandbox.io/s/nuxt-components-fdcw5?file=/components/LazyComponentA.vue

Expected behavior Components should render the same as if using them directly.

Additional context Add any other context about the problem here.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 14
  • Comments: 31 (2 by maintainers)

Most upvoted comments

Hi @hv-pul ,

<component> is intended to only be used with is value being a dynamic variable. And the variable being dynamic, Nuxt.js can’t determine which components to import.

We’ve been thinking of a possible implementation with automatic performance boost by lazy loading the components wanted this way :

<template>
  <!-- @components: A, B --> 
  <component :is="componentName" />
  <button @click="componentName = 'B'">Change Component</button>
</template>

<script>
export default {
  data () {
    return {
      componentName: 'A',
    }
  }
}
</script>

We specify with a comment that our component can dynamically be A or B (it could be any number of components)

It will automatically lazy import A and B, which means B will only be used (downloaded) if the button is clicked.

What do you think ?

Hi. Sorry this issue left unanswered. Actually by global option it is possible to use dynamic components.

// nuxt.config.js
export default {
  components: [{ path: "~/components", global: true }]
}
<template>
  <div>
    <h2>Nuxt Dynamic Components</h2>
    <div v-for="name in ['test1', 'test2']" :key="name">
      <component :is="name"></component>
    </div>
  </div>
</template>

(sandbox)

Please note that global option does not means components are added to main chunk but they are dynamically imported with webpack (see here and here)

This option is disabled by default purposefully because forcing webpack to make one async chunk per-component makes chunking less efficient (this is same technique used by @aerophobic module) so you have to only use for dirs/when dynamic components are necessary

Ok. I just wanted to confirm, that the dynamic components import works already even without specifying global: true. I tested on my Storyblok project, where I am getting the name/type of the component from API and the <component :is="variable"/> approach worked.

Hi @kevinmarrec,

thanks a lot for your explanation. I mistakenly thought that all components would be imported gloablly, but I see you parse the markup instead.

I have to say I’m not a big fan of the magic comment annotation. It does not seem very Vue-idiomatic to me. It would In that case I guess you could just as well use explicit imports or a dynamic pattern like this. And it would be very hard to keep track of which components you install where.

Three things come to my mind:

  • An optional NuxtComponent-component, in which all components are installed globally (as lazy imports). Usage would be just like the usual case: <NuxtComponent :is="MyComponent">, which would lazyload the MyComponent-chunk. That would mean that some unnecessary chunks are generated when NuxtComponent is used, but at least they would not be loaded, and it’s up users whether they decide to use it.

  • a global nuxt.config option where you can set the components that will be globally installed to the NuxtComponent (or even any component), ideally with the option to glob directories. As the chunks for any use of the <component>-Tag need to be generated anyway, even with the comment syntax you suggested, that would at least keep things in one place, so it seems less brittle to me. So something like this:

  components: {
    globalInstall: ['components/builder/**/*.vue', 'components/global/myComponent.vue']
  }
  • If you go with the comment-route, could you allow glob-patterns? That way at least it would be easier to handle something like page builder patterns, with a lot of dynamic components (could easily be dozens or more) that would need to go in the comment. So something like <!-- @components /components/builder/**/*.vue -->

// EDIT:

Or, option 4:

  • add a key similar to components to the component config:
export default {
  nuxtLazyComponents: ['ComponentA', 'ComponentB'],
  data() {…
}

Auto import dynamic components

@gilles6 @cavi21 @samuells It’s been some time, but I was able to write down the idea and how to solve this problem. You can checkout my explanation on medium.

Nuxt.js module(s)

I prepared a nuxt module to provide a NuxtDynamic component which works as stated before: @blokwise/dynamic

@samuells there is also an implementation for a wrapper which renders components based on storyblok schemas, checkout the @blokwise/blok module.

I like the 2nd option, available for any <component>, with an option to glob directories.

My personal use case is that I have a site that is built in CMS and I don’t know which sections / components will be used in advance.

A magic comment is rather strange.

Maybe something along those lines:

<template>
  <component :is="componentName" nuxtLazy />
  <button @click="componentName = 'B'">Change Component</button>
</template>

I would vote for option 4 of the proposal from @hv-pul with the nuxtLazyComponents (or whatever that prop is called) being a function returning the array of component names. afaik all components in a project are indexed. mapping that array of indexed components with the defined array in specific component with the nuxtLazyComponents defined will make it possible to lazy load the components as dynamic components easily (at least with dynamic imports when binding the imported component instead of it’s name to the :is prop of the dynamic component in the template)

Any tips on how to use this import('~/node_modules/UILibrary/${this.component_name}') ? The import route gets scrambled by I assume webpack and doesn’t work. In particular I am trying to do this with vuetify. It works when importing from ~/components but not when importing from node_modules

I would be definitely interested in some kind of way/pattern to define these dynamic components. Especially in my case using Storyblok CMS I don’t know often which components should I render. What I often know is a list of possibilities. So Option 4 looks like a way to me. 😇

It make sens to add comments like this. As in production it’s almost impossible to know before wich componnents will be there. In this case you will add all components in the bundler that is not possible.

Definitely cool. As i need it too ^^. I hope it i will come soon 😃

@kevinmarrec I wonder if there’s perhaps room to explore a similar approach to what content is doing (having a special directory that is treated as global components to always be loaded).

From https://content.nuxtjs.org/writing#table-of-contents :

image

@gilles6 you cannot dynamically load dynamic components as you try to do - by now at least. That’s what the whole issue is about 😉 However, I managed to build a workaround with the help of vue-lazy-hydration package. I can describe my approach to do so if you are interested.

If components that could potentially be lazy loaded need to be specified (which is reasonable), then I think a nuxtComponents option with an array of component names is preferable to the magic comment.

The exported object is where all the other options regarding component logic live, so that’s where I’d expect to specify this kind of thing.

in terms of having a small chunk as a possible i am not a fan of having by default like global components register for dynamics. I have like 130 components in my project so… that have a lot of dynamics dependencies.

I like the idea of comments clear and simple