JAVASCRIPT
Reusable Logic with Renderless Components and Scoped Slots in Vue 3
Discover how to build powerful renderless components in Vue 3 using scoped slots to share complex logic without dictating UI markup.
// components/MouseTracker.vue (Renderless Component)
<template>
<div>
<slot :x="x" :y="y" :hovering="isHovering"></slot>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const x = ref(0);
const y = ref(0);
const isHovering = ref(false);
const updateMouse = (event) => {
x.value = event.pageX;
y.value = event.pageY;
};
const setHovering = (value) => {
isHovering.value = value;
};
onMounted(() => {
window.addEventListener('mousemove', updateMouse);
// Optional: Add listeners for mouse enter/leave on the component's root element
// For more specific hovering behavior, you might need to target a specific element
// or wrap the slot content in a div and add listeners to it.
});
onUnmounted(() => {
window.removeEventListener('mousemove', updateMouse);
});
</script>
// App.vue (Example Usage 1: Displaying coordinates)
<template>
<h1>Mouse Tracker Example</h1>
<MouseTracker v-slot="{ x, y }">
<p>Mouse X: {{ x }}</p>
<p>Mouse Y: {{ y }}</p>
</MouseTracker>
<hr>
<!-- Example Usage 2: Different UI, same logic -->
<MouseTracker v-slot="{ x, y, hovering }">
<div class="hover-area" @mouseover="hovering = true" @mouseleave="hovering = false">
<h3>Mouse Position in Hover Area</h3>
<p v-if="hovering">You are hovering!</p>
<p v-else>Hover over this area</p>
<p>X: {{ x }}, Y: {{ y }}</p>
</div>
</MouseTracker>
</template>
<script setup>
import MouseTracker from './components/MouseTracker.vue';
</script>
<style>
.hover-area {
border: 2px dashed #42b983;
padding: 20px;
margin-top: 20px;
text-align: center;
min-height: 100px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
</style>
How it works: This snippet showcases a "renderless component" pattern in Vue 3, utilizing scoped slots to share logic without imposing any specific UI structure. The `MouseTracker` component manages the state and logic for tracking mouse coordinates (`x`, `y`) and whether the mouse is hovering. Instead of rendering its own HTML, it exposes this reactive data through a `<slot>` with props (e.g., `:x="x"`, `:y="y"`). Parent components then consume this logic by using `v-slot="{ x, y, hovering }"` and can render any UI they desire based on the provided data. This pattern promotes extreme reusability of complex behavior, allowing developers to separate concerns between data/logic and presentation effectively.