Nuxt/docs/content/2.guide/2.directory-structure/1.components.md
2022-11-08 12:47:46 +01:00

306 lines
7.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
navigation.icon: IconDirectory
title: "components"
description: "The components/ directory is where you put all your Vue components."
head.title: "Components"
---
# 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
export default defineNuxtConfig({
components: {
+ global: true,
+ dirs: ['~/components']
},
})
```
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 />
<!-- This renders the "span" element on the server side -->
<ClientOnly fallbackTag="span">
<!-- 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.
:: -->
## .client Components
If a component is meant to be rendered only client-side, you can add the `.client` suffix to your component.
```bash
| components/
--| Comments.client.vue
```
```html{}[pages/example.vue]
<template>
<div>
<!-- this component will only be rendered on client side -->
<Comments />
</div>
</template>
```
::alert{type=warning}
This feature only works with Nuxt auto-imports and `#components` imports. Explicitly importing these components from their real paths does not convert them into client-only components.
::
## .server Components
`.server` components are fallback components of `.client` components.
```bash
| components/
--| Comments.client.vue
--| Comments.server.vue
```
```html{}[pages/example.vue]
<template>
<div>
<!-- this component will render Comments.server server-side then Comments.client once mounted in client-side -->
<Comments />
</div>
</template>
```
## `<DevOnly>` Component
Nuxt provides the `<DevOnly>` component to render a component only during development.
The content will not be included in production builds and tree-shaken.
```html{}[pages/example.vue]
<template>
<div>
<Sidebar />
<DevOnly>
<!-- this component will only be rendered during development -->
<LazyDebugBar />
</DevOnly>
</div>
</template>
```
## 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"}
::