If you are reading this post, chances are that have encountered the following message while trying to access the slot.default()

Slot "default" invoked outside of the render function:
this will not track dependencies used in the slot. Invoke the slot function inside the render function instead. 

In this post, we are going to explain the reasoning behind the error and how to solve it, if you just want the solution you can just read the following sentence or jump at the last chapter of this post.

3 Revisions

TLTR; The slot invocation needs to happen within the render function or the template. To suppress the error you just need to move the code into a computed property or method called from within the template or render function.

What does “this will not track dependencies used in the slot” refer to?

The error message explain what the issue actually is behind the scene, but this is still not very clear to help us define what the issue is. In the following section, we are going to cover in details the reason behind the following part of the error:

this will not track dependencies used in the slot.

After some investigation, I made a reproducible code and understood the implication of using the `slots.default()` syntax outside of a render function. To understand the issue we need to freshen up our understanding of Vue reactivity system.

The Vue reactivity system allows us to declare properties, data and computed properties without the need to keep track of their changes. The reactivity system works behind the scene to make sure our variables are always up to date.

The most common case of a reactive feature within the Vue framework, is the use of computed. A computed is:

A computed properties refer to a variable that can be used to modify and manipulate datas and properties in your component in an efficient and reactive way.

A simple example of a computed property could be blog snippets, where we take a full blog post being passed as property and truncate it to a certain amount of character. Another more common one is a simple variable used to define a button text to either “show” or “hide” depending on the current state.

Computed properties are extremely useful not only because of their simple usage and powerful feature but also because they are extremely effective due to their caching strategy. These variables are cached until any of property of data used within them is changed.

So for example, the following property will never be run again until the value of “expanded” is changed.

const buttonText = computed( () => {
  return expanded.value ? 'Show less' : 'Show more';
});

The above method is not going to be triggered again unless the value expanded changes. The work that the Vue Js framework does behind the scene to watch the expanded variables is what is referred to as “track dependencies”.

As you may have already realised, the words “track dependencies” are the same that are mentioned within the error raised by the Vue framework while trying to access the slots. In fact, the error is provided to inform you that using the syntax `slots.default()` outside of a render function, will make the variable lose its reactivity and it will therefore not “track” any changes that could affect it.

If we re-consider the above example, losing the dependencies tracking would mean that no matter the value of expanded the button would not change

// The following code is for illustraction only and it is not the way in which you would write Vue
//  We are just assuming a variable with Tracking dependencies, that is what is happening to our slot
const expanded = ref( false ); //Broken Tracking

console.log(buttonText)
// OUTPUT: "Show more"

expanded.value = true;

console.log(buttonText)
// OUTPUT: "Show more" <-- This has not changed because Vue is not able to track the changes in the expanded.


Untracked variables are really not something that we want to have within our codebase and should be avoided at all costs. In the following section, we are going to analyse how to solve the issue and make sure that our “slot” is actually tracked.

How to ensure Vue slots track dependencies

It is now time to analyse what we can do to ensure that our slots have a reactive tracking system and do not fail to update.

The issue is solved by ensuring that our slot invocation is happening within the render function and or the template as mentioned in the error message:

Invoke the slot function inside the render function

We are now going to cover two different scenarios. The first is when using invoking the slot function while using the render function and the second is when using the <template> section of vue single file components.

Using slots with the render function

When using the slots in a component that has a render function, it is essential that we ensure the slot function is called within the “return” statement of our render function and not within the setup body.

// Bad example
import { h } from 'vue'

export default {
  setup( props, { slots } ) {
    const defaultSlot = slots.default();
    return () => h('div', defaultSlot)
  }
}

//Good example
import { h } from 'vue'

export default {
  setup( props, { slots } ) {
    return () => h('div', slots.default())
  }
}

Using slots when using a single file component (SFC)

If you are using the single file component and declare your HTML using the <template> block, you may be of the assumption that you do not have direct access to the render function, but this is not the case.

When I first encountered the issue, I spent some time trying to understand how to move my slots function within the render function, but after some coffee and a walk I remembered that the <template> tag is transformed into a render function from the compiler for us.

Understanding that both the <template> block and the render function are equivalents, help us massively to define the solution to our problem. In fact, to remove the warning and ensure that dependencies are tracked within our component, we need to ensure that the slot invocation is happening within the HTML (subsequently compiled by the framework into a render function).

Let’s cover an example to better understand the solution to our problems:


// BAD - It will not change if the slot changes
<template>
  <div :class="{ 'style-for-svg': isSvg }">
    <slot></slot>
  </div>
</template>

<script>
import { ref } from 'vue'

export default {
  setup( props, { slots } ) {
    const isSvg = ref( false );

    if( slots.default()[0].type === 'svg' ) {
      isSvg.value = true;
    }

    return {
      isSvg
    }
  }
}
</script>


//Good - It will change if slot changes
<template>
  <div :class="{ 'style-for-svg': $slots.default()[0].type === 'svg' }">
    <slot></slot>
  </div>
</template>

<script>

export default {
  setup( ) {
  }
}
</script>

Solving out the issue is quite simple. Adding the function call directly in the template is solving our problems. Unfortunately, the above solution does not seem very clean to read.

Luckily for you I have one last code snippets to share with you. In fact, while investigating I also realised that computed properties are also compiled as part of the render function and can be used to make the code more readable, and still keeping the variables reactive


<template>
  <div :class="{ 'style-for-svg': isSvg }">
    <slot></slot>
  </div>
</template>

<script>
import { computed } from 'vue'

export default {
  setup( ) {

    const isSvg = computed( () => {
      return slots.default()[0].type === 'svg';
    } );

    return {
       isSvg
    }

  }
}
</script>

Conclusion

It is not very common to require to access the slot function while developing your Vue components, but if you ever need to do so, I hope that the above solution saves you some time. Please post a comment below if this post was useful for you, and or let me know if there is any improvement or further information that you found, so that I can update the post and make sure the people benefit from your research.

Thank you again for reading my post, and I will wish you a great development time using the amazing Vue framework.

🤞 Don’t miss these tips!

No spam emails.. Pinky promise!