← Back to all snippets
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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs