mirror of
https://github.com/nuxt/nuxt.git
synced 2024-12-05 20:07:16 +00:00
447 lines
13 KiB
Markdown
447 lines
13 KiB
Markdown
---
|
|
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](/docs/guide/directory-structure/components) 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](/docs/guide/directory-structure/components) (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>
|
|
```
|
|
|
|
## Custom directories
|
|
|
|
By default, only the `~/components` directory is scanned. If you want to add other directories, or change how the components are scanned within a subfolder of this directory, you can add additional directories to the configuration:
|
|
|
|
```ts [nuxt.config.ts]
|
|
export default defineNuxtConfig({
|
|
components: [
|
|
// ~/calendar-module/components/event/Update.vue => <EventUpdate />
|
|
{ path: '~/calendar-module/components' },
|
|
|
|
// ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog />
|
|
{ path: '~/user-module/components', pathPrefix: false },
|
|
|
|
// ~/components/special-components/Btn.vue => <SpecialBtn />
|
|
{ path: '~/components/special-components', prefix: 'Special' },
|
|
|
|
// It's important that this comes last if you have overrides you wish to apply
|
|
// to sub-directories of `~/components`.
|
|
//
|
|
// ~/components/Btn.vue => <Btn />
|
|
// ~/components/base/Btn.vue => <BaseBtn />
|
|
'~/components'
|
|
]
|
|
})
|
|
```
|
|
|
|
::alert
|
|
Any nested directories need to be added first as they are scanned in order.
|
|
::
|
|
|
|
## Component extensions
|
|
|
|
By default, any file with an extension specified in the [extensions key of `nuxt.config.ts`](/docs/api/configuration/nuxt-config#extensions) is treated as a component.
|
|
If you need to restrict the file extensions that should be registered as components, you can use the extended form of the components directory declaration and its `extensions` key:
|
|
|
|
```diff
|
|
export default defineNuxtConfig({
|
|
components: [
|
|
{
|
|
path: '~/components',
|
|
+ extensions: ['.vue'],
|
|
}
|
|
]
|
|
})
|
|
```
|
|
|
|
## 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`.)
|
|
::
|
|
|
|
If you want to auto-import components based only on its name, not path, then you need to set `pathPrefix` option to `false` using extended form of the configuration object:
|
|
|
|
```diff
|
|
export default defineNuxtConfig({
|
|
components: [
|
|
{
|
|
path: '~/components',
|
|
+ pathPrefix: false,
|
|
},
|
|
],
|
|
});
|
|
```
|
|
|
|
This registers the components using the same strategy as used in Nuxt 2. For example, `~/components/Some/MyComponent.vue` will be usable as `<MyComponent>` and not `<SomeMyComponent>`.
|
|
|
|
## Dynamic Components
|
|
|
|
If you want to use the Vue `<component :is="someComputedComponent">` syntax, you need to use the `resolveComponent` helper provided by Vue or import the component directly from `#components` and pass it into `is` prop.
|
|
|
|
For example:
|
|
|
|
```vue
|
|
<script setup lang="ts">
|
|
import { SomeComponent } from '#components'
|
|
|
|
const MyButton = resolveComponent('MyButton')
|
|
</script>
|
|
|
|
<template>
|
|
<component :is="clickable ? MyButton : 'div'" />
|
|
<component :is="SomeComponent" />
|
|
</template>
|
|
```
|
|
|
|
::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]
|
|
<script>
|
|
export default {
|
|
data() {
|
|
return {
|
|
show: false
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div>
|
|
<h1>Mountains</h1>
|
|
<LazyMountainsList v-if="show" />
|
|
<button v-if="!show" @click="show = true">Show List</button>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## 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]
|
|
<script setup lang="ts">
|
|
import { NuxtLink, LazyMountainsList } from '#components'
|
|
const show = ref(false)
|
|
</script>
|
|
|
|
<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>
|
|
```
|
|
|
|
## `<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.
|
|
::
|
|
|
|
::alert{type=warning}
|
|
`.client` components are rendered only after being mounted. To access the rendered template using `onMounted()`, add `await nextTick()` in the callback of the `onMounted()` hook.
|
|
::
|
|
|
|
## .server Components
|
|
|
|
`.server` components can either be used on their own or paired with a `.client` component.
|
|
|
|
### Standalone server components
|
|
|
|
Standalone server components will always be rendered on the server. When their props update, this will result in a network request that will update the rendered HTML in-place.
|
|
|
|
:video-player{src="https://www.youtube.com/watch?v=u1yyXe86xJM"}
|
|
|
|
> A video made by [LearnVue](https://go.learnvue.co) for the Nuxt documentation.
|
|
|
|
Server components are currently experimental and in order to use them, you need to enable the 'component islands' feature in your nuxt.config:
|
|
|
|
```ts [nuxt.config.ts]
|
|
export default defineNuxtConfig({
|
|
experimental: {
|
|
componentIslands: true
|
|
}
|
|
})
|
|
```
|
|
|
|
Now you can register server-only components with the `.server` suffix and use them anywhere in your application automatically.
|
|
|
|
```bash
|
|
| components/
|
|
--| HighlightedMarkdown.server.vue
|
|
```
|
|
|
|
```html [pages/example.vue]
|
|
<template>
|
|
<div>
|
|
<!--
|
|
this will automatically be rendered on the server, meaning your markdown parsing + highlighting
|
|
libraries are not included in your client bundle.
|
|
-->
|
|
<HighlightedMarkdown markdown="# Headline" />
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
Server-only components use `<NuxtIsland>` under the hood, meaning that `lazy` prop and `#fallback` slot are both passed down to `<NuxtIsland>`.
|
|
|
|
::alert{type=info}
|
|
Slots can be interactive and are wrapped within a `<div>` with `display: contents;`
|
|
::
|
|
|
|
### Paired with a `.client` component
|
|
|
|
In this case, the `.server` + `.client` components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side.
|
|
|
|
```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>
|
|
```
|
|
|
|
::alert{type=warning}
|
|
It is essential that the client half of the component can 'hydrate' the server-rendered HTML. That is, it should render the same HTML on initial load, or you will experience a hydration mismatch.
|
|
::
|
|
|
|
## `<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 />
|
|
|
|
<!-- if you ever require to have a replacement during production -->
|
|
<!-- be sure to test these using `nuxt preview` -->
|
|
<template #fallback>
|
|
<div><!-- empty div for flex.justify-between --></div>
|
|
</template>
|
|
</DevOnly>
|
|
</div>
|
|
</template>
|
|
```
|
|
|
|
## `<NuxtClientFallback>` Component
|
|
|
|
Nuxt provides the `<NuxtClientFallback>` component to render its content on the client if any of its children trigger an error in SSR.
|
|
You can specify a `fallbackTag` to make it render a specific tag if it fails to render on the server.
|
|
|
|
```html [pages/example.vue]
|
|
<template>
|
|
<div>
|
|
<Sidebar />
|
|
<!-- this component will be rendered on client-side -->
|
|
<NuxtClientFallback fallback-tag="span">
|
|
<Comments />
|
|
<BrokeInSSR />
|
|
</NuxtClientFallback>
|
|
</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:
|
|
|
|
```ts
|
|
import { defineNuxtModule, createResolver } from '@nuxt/kit'
|
|
|
|
export default defineNuxtModule({
|
|
hooks: {
|
|
'components:dirs': (dirs) => {
|
|
const { resolve } = createResolver(import.meta.url)
|
|
// Add ./components dir to the list
|
|
dirs.push({
|
|
path: fileURLToPath(resolve('./components')),
|
|
prefix: 'awesome'
|
|
})
|
|
}
|
|
}
|
|
})
|
|
```
|
|
|
|
That's it! Now in your project, you can import your UI library as a Nuxt module in your `nuxt.config` file:
|
|
|
|
```ts [nuxt.config.ts]
|
|
export default defineNuxtConfig({
|
|
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="/docs/examples/features/auto-imports"}
|