Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Apply VueUse composables in Vue 3/Nuxt projects to replace custom implementations with battle-tested utilities.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/createReusableTemplate.md
1---2category: Component3outline: deep4---56# createReusableTemplate78Define and reuse template inside the component scope.910## Motivation1112It's common to have the need to reuse some part of the template. For example:1314```vue15<template>16<dialog v-if="showInDialog">17<!-- something complex -->18</dialog>19<div v-else>20<!-- something complex -->21</div>22</template>23```2425We'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 sometimes.2627So this function is made to provide a way for defining and reusing templates inside the component scope.2829## Usage3031In the previous example, we could refactor it to:3233```vue34<script setup lang="ts">35import { createReusableTemplate } from '@vueuse/core'3637const [DefineTemplate, ReuseTemplate] = createReusableTemplate()38</script>3940<template>41<DefineTemplate>42<!-- something complex -->43</DefineTemplate>4445<dialog v-if="showInDialog">46<ReuseTemplate />47</dialog>48<div v-else>49<ReuseTemplate />50</div>51</template>52```5354- `<DefineTemplate>` will register the template and renders nothing.55- `<ReuseTemplate>` will render the template provided by `<DefineTemplate>`.56- `<DefineTemplate>` must be used before `<ReuseTemplate>`.5758> **Note**: It's recommended to extract as separate components whenever possible. Abusing this function might lead to bad practices for your codebase.5960### Options API6162When using with [Options API](https://vuejs.org/guide/introduction.html#api-styles), you will need to define `createReusableTemplate` outside of the component setup and pass to the `components` option in order to use them in the template.6364```vue65<script>66import { createReusableTemplate } from '@vueuse/core'67import { defineComponent } from 'vue'6869const [DefineTemplate, ReuseTemplate] = createReusableTemplate()7071export default defineComponent({72components: {73DefineTemplate,74ReuseTemplate,75},76setup() {77// ...78},79})80</script>8182<template>83<DefineTemplate v-slot="{ data, msg, anything }">84<div>{{ data }} passed from usage</div>85</DefineTemplate>8687<ReuseTemplate :data="data" msg="The first usage" />88</template>89```9091### Passing Data9293You can also pass data to the template using slots:9495- Use `v-slot="..."` to access the data on `<DefineTemplate>`96- Directly bind the data on `<ReuseTemplate>` to pass them to the template9798```vue99<script setup lang="ts">100import { createReusableTemplate } from '@vueuse/core'101102const [DefineTemplate, ReuseTemplate] = createReusableTemplate()103</script>104105<template>106<DefineTemplate v-slot="{ data, msg, anything }">107<div>{{ data }} passed from usage</div>108</DefineTemplate>109110<ReuseTemplate :data="data" msg="The first usage" />111<ReuseTemplate :data="anotherData" msg="The second usage" />112<ReuseTemplate v-bind="{ data: something, msg: 'The third' }" />113</template>114```115116### TypeScript Support117118`createReusableTemplate` accepts a generic type to provide type support for the data passed to the template:119120```vue121<script setup lang="ts">122import { createReusableTemplate } from '@vueuse/core'123124// Comes with pair of `DefineTemplate` and `ReuseTemplate`125const [DefineFoo, ReuseFoo] = createReusableTemplate<{ msg: string }>()126127// You can create multiple reusable templates128const [DefineBar, ReuseBar] = createReusableTemplate<{ items: string[] }>()129</script>130131<template>132<DefineFoo v-slot="{ msg }">133<!-- `msg` is typed as `string` -->134<div>Hello {{ msg.toUpperCase() }}</div>135</DefineFoo>136137<ReuseFoo msg="World" />138139<!-- @ts-expect-error Type Error! -->140<ReuseFoo :msg="1" />141</template>142```143144Optionally, if you are not a fan of array destructuring, the following usages are also legal:145146```vue147<script setup lang="ts">148import { createReusableTemplate } from '@vueuse/core'149150const { define: DefineFoo, reuse: ReuseFoo } = createReusableTemplate<{151msg: string152}>()153</script>154155<template>156<DefineFoo v-slot="{ msg }">157<div>Hello {{ msg.toUpperCase() }}</div>158</DefineFoo>159160<ReuseFoo msg="World" />161</template>162```163164```vue165<script setup lang="ts">166import { createReusableTemplate } from '@vueuse/core'167168const TemplateFoo = createReusableTemplate<{ msg: string }>()169</script>170171<template>172<TemplateFoo.define v-slot="{ msg }">173<div>Hello {{ msg.toUpperCase() }}</div>174</TemplateFoo.define>175176<TemplateFoo.reuse msg="World" />177</template>178```179180::: warning181Passing boolean props without `v-bind` is not supported. See the [Caveats](#boolean-props) section for more details.182:::183184### Props and Attributes185186By default, all props and attributes passed to `<ReuseTemplate>` will be passed to the template. If you don't want certain props to be passed to the DOM, you need to define the runtime props:187188```ts189import { createReusableTemplate } from '@vueuse/core'190191const [DefineTemplate, ReuseTemplate] = createReusableTemplate({192props: {193msg: String,194enable: Boolean,195}196})197```198199If you don't want to pass any props to the template, you can pass the `inheritAttrs` option:200201```ts202import { createReusableTemplate } from '@vueuse/core'203204const [DefineTemplate, ReuseTemplate] = createReusableTemplate({205inheritAttrs: false,206})207```208209### Passing Slots210211It's also possible to pass slots back from `<ReuseTemplate>`. You can access the slots on `<DefineTemplate>` from `$slots`:212213```vue214<script setup lang="ts">215import { createReusableTemplate } from '@vueuse/core'216217const [DefineTemplate, ReuseTemplate] = createReusableTemplate()218</script>219220<template>221<DefineTemplate v-slot="{ $slots, otherProp }">222<div some-layout>223<!-- To render the slot -->224<component :is="$slots.default" />225</div>226</DefineTemplate>227228<ReuseTemplate>229<div>Some content</div>230</ReuseTemplate>231<ReuseTemplate>232<div>Another content</div>233</ReuseTemplate>234</template>235```236237## Caveats238239### Boolean props240241As opposed to Vue's behavior, props defined as `boolean` that were passed without `v-bind` or absent will be resolved into an empty string or `undefined` respectively:242243```vue244<script setup lang="ts">245import { createReusableTemplate } from '@vueuse/core'246247const [DefineTemplate, ReuseTemplate] = createReusableTemplate<{248value?: boolean249}>()250</script>251252<template>253<DefineTemplate v-slot="{ value }">254{{ typeof value }}: {{ value }}255</DefineTemplate>256257<ReuseTemplate :value="true" />258<!-- boolean: true -->259260<ReuseTemplate :value="false" />261<!-- boolean: false -->262263<ReuseTemplate value />264<!-- string: -->265266<ReuseTemplate />267<!-- undefined: -->268</template>269```270271## References272273This function is migrated from [vue-reuse-template](https://github.com/antfu/vue-reuse-template).274275Existing Vue discussions/issues about reusing template:276277- [Discussion on Reusing Templates](https://github.com/vuejs/core/discussions/6898)278279Alternative Approaches:280281- [Vue Macros - `namedTemplate`](https://vue-macros.sxzz.moe/features/named-template.html)282- [`unplugin-vue-reuse-template`](https://github.com/liulinboyi/unplugin-vue-reuse-template)283284## Type Declarations285286```ts287type ObjectLiteralWithPotentialObjectLiterals = Record<288string,289Record<string, any> | undefined290>291type GenerateSlotsFromSlotMap<292T extends ObjectLiteralWithPotentialObjectLiterals,293> = {294[K in keyof T]: Slot<T[K]>295}296export type DefineTemplateComponent<297Bindings extends Record<string, any>,298MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,299> = DefineComponent & {300new (): {301$slots: {302default: (303_: Bindings & {304$slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>305},306) => any307}308}309}310export type ReuseTemplateComponent<311Bindings extends Record<string, any>,312MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,313> = DefineComponent<Bindings> & {314new (): {315$slots: GenerateSlotsFromSlotMap<MapSlotNameToSlotProps>316}317}318export type ReusableTemplatePair<319Bindings extends Record<string, any>,320MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals,321> = [322DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>,323ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>,324] & {325define: DefineTemplateComponent<Bindings, MapSlotNameToSlotProps>326reuse: ReuseTemplateComponent<Bindings, MapSlotNameToSlotProps>327}328export interface CreateReusableTemplateOptions<329Props extends Record<string, any>,330> {331/**332* Inherit attrs from reuse component.333*334* @default true335*/336inheritAttrs?: boolean337/**338* Props definition for reuse component.339*/340props?: ComponentObjectPropsOptions<Props>341}342/**343* This function creates `define` and `reuse` components in pair,344* It also allow to pass a generic to bind with type.345*346* @see https://vueuse.org/createReusableTemplate347*348* @__NO_SIDE_EFFECTS__349*/350export declare function createReusableTemplate<351Bindings extends Record<string, any>,352MapSlotNameToSlotProps extends ObjectLiteralWithPotentialObjectLiterals =353Record<"default", undefined>,354>(355options?: CreateReusableTemplateOptions<Bindings>,356): ReusableTemplatePair<Bindings, MapSlotNameToSlotProps>357```358