| Category | Component |
|---|---|
| Export Size | 734 B |
| Last Changed | last week |
Define and reuse template inside the component scope.
It's common to have the need to reuse some part of the template. For example:
<template>
<dialog v-if="showInDialog">
<!-- something complex -->
</dialog>
<div v-else>
<!-- something complex -->
</div>
</template>
We'd like to reuse our code as much as possible. So normally we might need to extract those duplicated parts into a component. However, in a separated component you lose the ability to access the local bindings. Defining props and emits for them can be tedious sometime.
So this function is made to provide a way for defining and reusing templates inside the component scope.
In the previous example, we could refactor it to:
<script setup>
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate>
<!-- something complex -->
</DefineTemplate>
<dialog v-if="showInDialog">
<ReuseTemplate />
</dialog>
<div v-else>
<ReuseTemplate />
</div>
</template>
<DefineTemplate> will register the template and renders nothing.<ReuseTemplate> will render the template provided by <DefineTemplate>.<DefineTemplate> must be used before <ReuseTemplate>.Note: It's recommended to extract as separate components whenever possible. Abusing this function might lead to bad practices for your codebase.
You can also pass data to the template using slots:
v-slot="..." to access the data on <DefineTemplate>
<ReuseTemplate> to pass them to the template<script setup>
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate v-slot="{ data, msg, anything }">
<div>{{ data }} passed from usage</div>
</DefineTemplate>
<ReuseTemplate :data="data" msg="The first usage" />
<ReuseTemplate :data="anotherData" msg="The second usage" />
<ReuseTemplate v-bind="{ data: something, msg: 'The third' }" />
</template>
createReusableTemplate accepts a generic type to provide type support for the data passed to the template:
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'
// Comes with pair of `DefineTemplate` and `ReuseTemplate`
const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()
// You can create multiple reusable templates
const [DefineBar, ReuseBar] = createReusableTemplate<{ items: string[] }>()
</script>
<template>
<DefineFoo v-slot="{ msg }">
<!-- `msg` is typed as `string` -->
<div>Hello {{ msg.toUpperCase() }}</div>
</DefineFoo>
<ReuseFoo msg="World" />
<!-- @ts-expect-error Type Error! -->
<ReuseFoo :msg="1" />
</template>
Optionally, if you are not a fan of array destructuring, the following usages are also legal:
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'
const {
define: DefineFoo,
reuse: ReuseFoo,
} = createReusableTemplate<{ msg: string }>()
</script>
<template>
<DefineFoo v-slot="{ msg }">
<div>Hello {{ msg.toUpperCase() }}</div>
</DefineFoo>
<ReuseFoo msg="World" />
</template>
<script setup lang="ts">
import { createReusableTemplate } from '@vueuse/core'
const TemplateFoo = createReusableTemplate<{ msg: string }>()
</script>
<template>
<TemplateFoo.define v-slot="{ msg }">
<div>Hello {{ msg.toUpperCase() }}</div>
</TemplateFoo.define>
<TemplateFoo.reuse msg="World" />
</template>
Dot notation is not supported in Vue 2.
It's also possible to pass slots back from <ReuseTemplate>. You can access the slots on <DefineTemplate> from $slots:
<script setup>
import { createReusableTemplate } from '@vueuse/core'
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>
<template>
<DefineTemplate v-slot="{ $slots, otherProp }">
<div some-layout>
<!-- To render the slot -->
<component :is="$slots.default" />
</div>
</DefineTemplate>
<ReuseTemplate>
<div>Some content</div>
</ReuseTemplate>
<ReuseTemplate>
<div>Another content</div>
</ReuseTemplate>
</template>
Passing slots does not work in Vue 2.
This function is migrated from vue-reuse-template.
Existing Vue discussions/issues about reusing template:
Alternative Approaches:
export type DefineTemplateComponent<
Bindings extends object,
Slots extends Record<string, Slot | undefined>
> = DefineComponent<{}> & {
new (): {
$slots: {
default(
_: Bindings & {
$slots: Slots
}
): any
}
}
}
export type ReuseTemplateComponent<
Bindings extends object,
Slots extends Record<string, Slot | undefined>
> = DefineComponent<Bindings> & {
new (): {
$slots: Slots
}
}
export type ReusableTemplatePair<
Bindings extends object,
Slots extends Record<string, Slot | undefined>
> = [
DefineTemplateComponent<Bindings, Slots>,
ReuseTemplateComponent<Bindings, Slots>
] & {
define: DefineTemplateComponent<Bindings, Slots>
reuse: ReuseTemplateComponent<Bindings, Slots>
}
/**
* This function creates `define` and `reuse` components in pair,
* It also allow to pass a generic to bind with type.
*
* @see https://vueuse.org/createReusableTemplate
*/
export declare function createReusableTemplate<
Bindings extends object,
Slots extends Record<string, Slot | undefined> = Record<
string,
Slot | undefined
>
>(): ReusableTemplatePair<Bindings, Slots>
© 2019-present Anthony Fu
Licensed under the MIT License.
https://vueuse.org/core/createReusableTemplate/