What if you could reuse complex UI behavior without being stuck with one look?
Why Renderless components in Vue? - Purpose & Use Cases
Imagine building a UI where you want to reuse logic like dropdown behavior or form validation, but each place needs a different look and structure.
You try copying and pasting the same code and changing styles everywhere.
Manually copying UI logic with different designs leads to repeated code, bugs, and hard-to-maintain projects.
Changing behavior means updating many places, risking inconsistencies and errors.
Renderless components let you share the logic without forcing any UI.
You provide your own template while the component handles all the behavior behind the scenes.
const Dropdown = { template: '<div>...</div>', methods: { open() { /* ... */ } } } // repeated with style changesconst DropdownLogic = { setup() { function open() { /* ... */ } function close() { /* ... */ } return { open, close } } } // no template, user provides UIYou can build flexible, reusable logic that works with any design you want, making your code cleaner and easier to update.
A dropdown menu component that manages open/close state and keyboard navigation but lets you design the button and list however you want.
Renderless components separate logic from UI.
They prevent code duplication and bugs.
They let you reuse behavior with any design.
Practice
renderless component in Vue?Solution
Step 1: Understand the concept of renderless components
Renderless components are designed to provide reusable logic without producing any visible UI themselves.Step 2: Compare options with the concept
Options A, B, and D describe unrelated Vue features or behaviors. Only To share logic without rendering any UI elements correctly describes the purpose of renderless components.Final Answer:
To share logic without rendering any UI elements -> Option AQuick Check:
Renderless components = share logic only [OK]
- Thinking renderless components render UI
- Confusing with routing or data fetching
- Assuming they provide default templates
Solution
Step 1: Identify renderless component pattern
Renderless components use slots to pass data and methods without rendering UI elements themselves.Step 2: Analyze each option
<template><slot :data="someData" /></template>uses a slot with a prop to pass data, which is the correct renderless pattern. Options A and D render UI elements, and C returns null but does not use slots.Final Answer:
<template><slot :data="someData" /></template> -> Option AQuick Check:
Renderless = slot with data prop [OK]
- Including UI elements inside renderless components
- Not using slots to expose data
- Returning null without slots
<RenderlessCounter v-slot="{ count, increment }">
<button @click="increment">Count: {{ count }}</button>
</RenderlessCounter>
What will happen when the button is clicked?
Solution
Step 1: Understand slot props usage
The renderless component exposescountandincrementvia slot props, which the parent uses to render a button and handle clicks.Step 2: Analyze button click effect
Clicking the button callsincrement, which increasescount. The button text updates because it uses{{ count }}.Final Answer:
The count value increases and the button text updates -> Option DQuick Check:
Slot props update UI on click [OK]
- Assuming renderless means no UI updates
- Thinking increment is undefined
- Confusing renderless with no interaction
<template>
<slot>Default</slot>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(0)
function increment() {
count++
}
</script>
Solution
Step 1: Check reactive ref usage
In Vue 3, refs must be updated viacount.value++, notcount++.Step 2: Verify other parts
Slot usage without a name is valid for default slot. Template can have a single root slot. Ref is properly imported from Vue.Final Answer:
Increment function incorrectly updates count with count++ instead of count.value++ -> Option CQuick Check:
Update ref with .value [OK]
- Updating ref directly without .value
- Assuming slot needs a name
- Thinking template must wrap slot in div
Solution
Step 1: Identify renderless component pattern
The component must not render UI but expose reactive data via slot props.Step 2: Analyze each option for correctness
<template><slot :x="x" :y="y" /></template> <script setup> let x = 0 let y = 0 window.addEventListener('mousemove', e => { x = e.clientX y = e.clientY }) </script>uses non-reactive plain variables, so position won't update reactively.<template><div>Mouse at {{ x }}, {{ y }}</div></template> <script setup> const x = ref(0) const y = ref(0) window.addEventListener('mousemove', e => { x.value = e.clientX y.value = e.clientY }) </script>renders UI, so not renderless.<template><slot /></template> <script setup> import { reactive } from 'vue' const position = reactive({ x: 0, y: 0 }) window.addEventListener('mousemove', e => { position.x = e.clientX position.y = e.clientY }) </script>exposes slot without props, so no data passed.<template><slot :pos="position" /></template> <script setup> import { reactive } from 'vue' const position = reactive({ x: 0, y: 0 }) window.addEventListener('mousemove', e => { position.x = e.clientX position.y = e.clientY }) </script>exposes reactive position object via slot prop, clean and correct.Final Answer:
Expose reactive position object via slot prop without rendering UI -> Option BQuick Check:
Renderless = slot with reactive data [OK]
- Rendering UI inside renderless component
- Not passing data via slot props
- Passing data but not reactive or incomplete
Ready to prove it?
Pass the test and this pattern moves unknown
