Now that you know all the NOVA LAB module storage tools, let's see how to combine them into advanced patterns - like a complete station logistics system.
A renderless component is a component that has no template of its own - it only provides logic through a scoped slot. The parent has full control over rendering.
1<!-- MissionTimer.vue (renderless) -->
2<script setup>
3import { ref, onMounted, onUnmounted } from 'vue'
4
5const count = ref(0)
6const isRunning = ref(false)
7let interval = null
8
9const start = () => {
10 if (!isRunning.value) {
11 isRunning.value = true
12 interval = setInterval(() => count.value++, 1000)
13 }
14}
15
16const stop = () => {
17 isRunning.value = false
18 clearInterval(interval)
19}
20
21const reset = () => {
22 count.value = 0
23 stop()
24}
25
26onUnmounted(() => clearInterval(interval))
27</script>
28
29<template>
30 <slot
31 :count="count"
32 :isRunning="isRunning"
33 :start="start"
34 :stop="stop"
35 :reset="reset"
36 />
37</template>
38
39<!-- Usage with any UI -->
40<MissionTimer v-slot="{ count, isRunning, start, stop, reset }">
41 <div class="timer">
42 <h2>{{ count }}s</h2>
43 <button v-if="!isRunning" @click="start">Start</button>
44 <button v-else @click="stop">Pause</button>
45 <button @click="reset">Reset</button>
46 </div>
47</MissionTimer>Slot names can be dynamic:
1<script setup>
2import { ref } from 'vue'
3const activeSection = ref('header')
4</script>
5
6<template>
7 <StationPanel>
8 <template v-slot:[activeSection]>
9 <p>Dynamic content for {{ activeSection }}</p>
10 </template>
11 </StationPanel>
12</template>Checking which slots have been provided:
1<script setup>
2import { useSlots } from 'vue'
3
4const slots = useSlots()
5
6// Check if a slot exists
7const hasHeader = !!slots.header
8const hasFooter = !!slots.footer
9</script>
10
11<template>
12 <div class="panel">
13 <header v-if="slots.header">
14 <slot name="header" />
15 </header>
16 <main>
17 <slot />
18 </main>
19 <footer v-if="slots.footer">
20 <slot name="footer" />
21 </footer>
22 </div>
23</template>An intermediate component can forward slots further down:
1<!-- Wrapper.vue -->
2<template>
3 <InnerComponent>
4 <template v-for="(_, name) in $slots" #[name]="slotProps">
5 <slot :name="name" v-bind="slotProps || {}" />
6 </template>
7 </InnerComponent>
8</template>| Pattern | When to Use | |---------|------------| | Props | Simple data (string, number, boolean) | | Default slot | Injecting arbitrary HTML | | Named slots | Multiple sections to fill | | Scoped slots | Rendering dependent on child's data | | Renderless | Logic only, full UI control | | Dynamic components | Switching between components | | Teleport | Rendering outside the DOM hierarchy | | Async + Suspense | Lazy loading heavy components |