mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-30 01:17:16 +00:00
4e2667fcb7
Co-authored-by: Pooya Parsa <pooya@pi0.io>
249 lines
6.3 KiB
Markdown
249 lines
6.3 KiB
Markdown
---
|
||
icon: IconDirectory
|
||
title: 'components'
|
||
head.title: Components directory
|
||
---
|
||
|
||
# Components directory
|
||
|
||
The `components/` directory is where you put all your Vue components which can then be imported inside your pages or other components ([learn more](https://vuejs.org/guide/essentials/component-basics.html#components-basics)).
|
||
|
||
Nuxt automatically imports any components in your `components/` directory (along with components that are registered by any modules you may be using).
|
||
|
||
```bash
|
||
| components/
|
||
--| TheHeader.vue
|
||
--| TheFooter.vue
|
||
```
|
||
|
||
```html{}[layouts/default.vue]
|
||
<template>
|
||
<div>
|
||
<TheHeader />
|
||
<slot />
|
||
<TheFooter />
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
## Component Names
|
||
|
||
If you have a component in nested directories such as:
|
||
|
||
```bash
|
||
| components/
|
||
--| base/
|
||
----| foo/
|
||
------| Button.vue
|
||
```
|
||
|
||
... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be:
|
||
|
||
```html
|
||
<BaseFooButton />
|
||
```
|
||
|
||
::alert
|
||
For clarity, we recommend that the component's filename matches its name. (So, in the example above, you could rename `Button.vue` to be `BaseFooButton.vue`.)
|
||
::
|
||
|
||
## Dynamic components
|
||
|
||
If you want to use the Vue `<component :is="someComputedComponent">` syntax, then you will need to use the `resolveComponent` helper provided by Vue.
|
||
|
||
For example:
|
||
|
||
```vue
|
||
<template>
|
||
<component :is="clickable ? MyButton : 'div'" />
|
||
</template>
|
||
|
||
<script setup>
|
||
const MyButton = resolveComponent('MyButton')
|
||
</script>
|
||
```
|
||
|
||
::alert{type=warning}
|
||
If you are using `resolveComponent` to handle dynamic components, make sure not to insert anything but the name of the component, which must be a string and not a variable.
|
||
::
|
||
|
||
Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application.
|
||
|
||
```diff
|
||
import { defineNuxtConfig } from 'nuxt'
|
||
|
||
export default defineNuxtConfig({
|
||
components: {
|
||
+ global: true,
|
||
+ dirs: ['~/components']
|
||
},
|
||
})
|
||
```
|
||
|
||
::StabilityEdge{title="Automatic global components"}
|
||
In the current version, components in `~/components/global` are not yet auto-registered.
|
||
::
|
||
|
||
You can also selectively register some components globally by placing them in a `~/components/global` directory.
|
||
|
||
::alert{type=info}
|
||
The `global` option can also be set per component directory.
|
||
::
|
||
|
||
## Dynamic Imports
|
||
|
||
To dynamically import a component (also known as lazy-loading a component) all you need to do is add the `Lazy` prefix to the component's name.
|
||
|
||
```html{}[layouts/default.vue]
|
||
<template>
|
||
<div>
|
||
<TheHeader />
|
||
<slot />
|
||
<LazyTheFooter />
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
This is particularly useful if the component is not always needed. By using the `Lazy` prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size.
|
||
|
||
```html{}[pages/index.vue]
|
||
<template>
|
||
<div>
|
||
<h1>Mountains</h1>
|
||
<LazyMountainsList v-if="show" />
|
||
<button v-if="!show" @click="show = true">Show List</button>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
data() {
|
||
return {
|
||
show: false
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
```
|
||
|
||
## Direct imports
|
||
|
||
You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality.
|
||
|
||
```html{}[pages/index.vue]
|
||
<template>
|
||
<div>
|
||
<h1>Mountains</h1>
|
||
<LazyMountainsList v-if="show" />
|
||
<button v-if="!show" @click="show = true">Show List</button>
|
||
<NuxtLink to="/">Home</NuxtLink>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { NuxtLink, LazyMountainsList } from '#components'
|
||
const show = ref(false)
|
||
</script>
|
||
```
|
||
|
||
## `<ClientOnly>` Component
|
||
|
||
Nuxt provides the `<ClientOnly>` component for purposely rendering a component only on client side. To import a component only on the client, register the component in a client-side only plugin.
|
||
|
||
```html{}[pages/example.vue]
|
||
<template>
|
||
<div>
|
||
<Sidebar />
|
||
<ClientOnly>
|
||
<!-- this component will only be rendered on client-side -->
|
||
<Comments />
|
||
</ClientOnly>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
Use a slot as fallback until `<ClientOnly>` is mounted on client side.
|
||
|
||
```html{}[pages/example.vue]
|
||
<template>
|
||
<div>
|
||
<Sidebar />
|
||
<ClientOnly>
|
||
<!-- this component will only be rendered on client side -->
|
||
<Comments />
|
||
<template #fallback>
|
||
<!-- this will be rendered on server side -->
|
||
<p>Loading comments...</p>
|
||
</template>
|
||
</ClientOnly>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
<!-- TODO: Add back after passing treeshakeClientOnly experiment -->
|
||
<!-- ::alert{type=warning}
|
||
Make sure not to _nest_ `<ClientOnly>` components or other client-only components. Nuxt performs an optimization to remove the contents of these components from the server-side render, which can break in this case.
|
||
:: -->
|
||
|
||
## Library Authors
|
||
|
||
Making Vue component libraries with automatic tree-shaking and component registration is super easy ✨
|
||
|
||
You can use the `components:dirs` hook to extend the directory list without requiring user configuration in your Nuxt module.
|
||
|
||
Imagine a directory structure like this:
|
||
|
||
```bash
|
||
| node_modules/
|
||
---| awesome-ui/
|
||
------| components/
|
||
---------| Alert.vue
|
||
---------| Button.vue
|
||
------| nuxt.js
|
||
| pages/
|
||
---| index.vue
|
||
| nuxt.config.js
|
||
```
|
||
|
||
Then in `awesome-ui/nuxt.js` you can use the `components:dirs` hook:
|
||
|
||
```js
|
||
import { defineNuxtModule } from '@nuxt/kit'
|
||
import { fileURLToPath } from 'node:url'
|
||
|
||
export default defineNuxtModule({
|
||
hooks: {
|
||
'components:dirs'(dirs) {
|
||
// Add ./components dir to the list
|
||
dirs.push({
|
||
path: fileURLToPath(new URL('./components', import.meta.url)),
|
||
prefix: 'awesome'
|
||
})
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
That's it! Now in your project, you can import your UI library as a Nuxt module in your `nuxt.config` file:
|
||
|
||
```js
|
||
export default {
|
||
modules: ['awesome-ui/nuxt']
|
||
}
|
||
```
|
||
|
||
... and directly use the module components (prefixed with `awesome-`) in our `pages/index.vue`:
|
||
|
||
```vue
|
||
<template>
|
||
<div>
|
||
My <AwesomeButton>UI button</AwesomeButton>!
|
||
<awesome-alert>Here's an alert!</awesome-alert>
|
||
</div>
|
||
</template>
|
||
```
|
||
|
||
It will automatically import the components only if used and also support HMR when updating your components in `node_modules/awesome-ui/components/`.
|
||
|
||
:LinkExample{link="/examples/auto-imports/components"}
|