Frontend Development 20 min read

How Vue.js Implements Slots: Deep Dive into $slots, renderSlot, and v-slot

This article provides a comprehensive analysis of Vue.js slot mechanisms, covering normal slots, scoped slots, and the v-slot syntax, with detailed code examples and explanations of the underlying rendering functions and compilation processes.

Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
Tencent IMWeb Frontend Team
How Vue.js Implements Slots: Deep Dive into $slots, renderSlot, and v-slot

This article analyzes how the commonly used Slots feature in Vue.js is designed and implemented, focusing on three parts: normal slots, scoped slots, and the v-slot syntax introduced in Vue.js 2.6.x.

1 Normal Slots

First, a simple example of using Slots is presented.

<code>&lt;template&gt;
  &lt;div class="slot-demo"&gt;
    &lt;slot&gt;this is slot default content text.&lt;/slot&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code>

Rendering this component produces the following result:

Next, we replace the content inside the slot.

&lt;slot-demo&gt;this is slot custom content.&lt;/slot-demo&gt;

After re‑rendering, the result is:

To understand the underlying logic, we examine Vue.js internal implementation of Slots.

1.1 vm.$slots

The

$slots

property on a component instance is defined as:

<code>$slots: { [key: string]: Array<VNode> };</code>

Printing

$slots

for the example above yields an object representation of the slot content.

1.2 renderSlot

The core rendering function for a slot is:

<code>export function renderSlot (
  name: string, // slot name
  fallback: ?Array<VNode>, // default content VNode array
  props: ?Object, // props object
  bindObject: ?Object // v‑bind object
): ?Array<VNode> {}
</code>

For a normal slot, Vue.js executes:

<code>const slotNodes = this.$slots[name]
nodes = slotNodes || fallback
return nodes
</code>

1.3 resolveSlots

The function that builds the

$slots

object is:

<code>export function resolveSlots (
  children: ?Array<VNode>, // parent vnode children
  context: ?Component // parent component instance
): { [key: string]: Array<VNode> } {}
</code>

It creates an empty

slots

object, returns it if there are no children, otherwise iterates over the children, assigning them to named slots or the default slot, and finally removes slots that contain only whitespace.

<code>// ignore slots that contain only whitespace
for (const name in slots) {
  if (slots[name].every(isWhitespace)) {
    delete slots[name]
  }
}
return slots
</code>

1.4 initRender

The

initRender

function initializes

vm.$slots

by calling

resolveSlots

:

<code>const options = vm.$options
const parentVnode = vm.$vnode = options._parentVnode // placeholder node in parent tree
const renderContext = parentVnode && parentVnode.context
vm.$slots = resolveSlots(options._renderChildren, renderContext)
</code>

1.5 genSlot

The code generator creates the render function for a slot:

<code>function genSlot (el: ASTElement, state: CodegenState): string {
  const slotName = el.slotName || '"default"'
  const children = genChildren(el, state)
  let res = `_t(${slotName}${children ? `,${children}` : ''})`
  const attrs = el.attrs && `{${el.attrs.map(a => `${camelize(a.name)}:${a.value}`).join(',')}}`
  const bind = el.attrsMap['v-bind']
  if ((attrs || bind) && !children) {
    res += `,null`
  }
  if (attrs) {
    res += `,${attrs}`
  }
  if (bind) {
    res += `${attrs ? '' : ',null'},${bind}`
  }
  return res + ')'
}
</code>

2 Scoped Slots

Scoped slots expose data from the child component to the parent. The component interface defines:

<code>$scopedSlots: { [key: string]: () => VNodeChildren };</code>

Example component:

<code>&lt;template&gt;
  &lt;div class="slot-demo"&gt;
    &lt;slot text="this is a slot demo , " :msg="msg"&gt;&lt;/slot&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
  name: 'SlotDemo',
  data () {
    return { msg: 'this is scoped slot content.' }
  }
}
&lt;/script&gt;
</code>

Parent usage with a scoped slot:

<code>&lt;template&gt;
  &lt;slot-demo&gt;
    &lt;template slot-scope="scope"&gt;
      &lt;p&gt;{{ scope.text }}{{ scope.msg }}&lt;/p&gt;
    &lt;/template&gt;
  &lt;/slot-demo&gt;
&lt;/template&gt;
</code>

The compiled render function for the parent includes a call to

_u

(resolveScopedSlots) and creates a function that receives the slot scope object.

<code>with(this){
  return _c('div', {staticClass:'parent-slot'}, [_c('slot-demo', {scopedSlots: _u([
    {key:'default', fn: function(scope){
      return [_c('p', [_v(_s(scope.text))]), _c('p', [_v(_s(scope.msg))])]
    }}])}])
}
</code>

3 v-slot Syntax (Vue 2.6.x)

Vue 2.6 introduced the

v-slot

directive as a syntactic sugar for scoped slots. The basic usage is:

<code>&lt;slot-demo&gt;
  &lt;template v-slot:demo&gt;this is custom slot.&lt;/template&gt;
  &lt;template v-slot="scope"&gt;
    &lt;p&gt;{{ scope.text }}{{ scope.msg }}&lt;/p&gt;
  &lt;/template&gt;
&lt;/slot-demo&gt;
</code>

Internally, the compiler parses

v-slot

on

template

elements or components, extracts the slot name and scope, and stores the content in the AST as a scoped slot. The processing functions have been renamed (e.g.,

processSlot

processSlotContent

) but the overall logic remains the same, with added handling for dynamic slot arguments.

Key helper regular expressions and functions include:

dynamicArgRE = /^\[.*\]$/

– matches dynamic slot arguments like

[item]

.

slotRE = /^v-slot(:|$)|^#/

– detects

v-slot

directives.

getAndRemoveAttrByRegex

– extracts and removes attributes matching a given regex.

getSlotName

– parses the slot name and determines if it is dynamic.

When a

v-slot

is found on a

template

, the compiler creates a new AST element with

slotTarget

,

slotTargetDynamic

, and

slotScope

properties. When used on a component, the component’s children are moved into a generated

template

node that becomes the default slot.

During code generation,

genSlot

now merges static and dynamic attributes and uses

genProps

to produce the appropriate render‑function string, supporting dynamic slot arguments.

Overall, Vue.js’s slot system evolves from simple content insertion (

$slots

) to powerful scoped slots (

$scopedSlots

) and finally to the concise

v-slot

syntax, all while maintaining backward compatibility through internal normalization functions.

Frontend DevelopmentVue.jsSlotsScoped Slotsv-slot
Tencent IMWeb Frontend Team
Written by

Tencent IMWeb Frontend Team

IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.