| |

Vue.js 37 🟢 Dynamic Slot Names

A slot name is usually a fixed string. The <slot name="header" /> in the child matches the <template #header> in the parent, and the name is the same in both places. But the name is not always known at the time the template is written. A table component renders the columns from the data, and the parent wants to provide the custom cell for the specific column. A layout component renders the regions from the configuration, and the parent wants to fill the region by the runtime name. A form renders the fields from the schema, and the parent wants to control the rendering of each field. The dynamic slot names solve the problem: the name is the JavaScript expression, and the parent targets the slot with the #[dynamicName] syntax.

The dynamic name is the expression in the brackets. The <template #[slotName]> is the parent’s usage, and the slotName is the parent’s data. The <slot :name="dynamicName" /> is the child’s usage, and the dynamicName is the child’s data. The two must evaluate to the same string for the slot to match. The expression is evaluated in the scope where it appears: the parent’s expression in the parent’s scope, and the child’s expression in the child’s scope. The two scopes are separate, and the string is the contract between them.

The dynamic name has the several rules. The v-slot directive with the dynamic argument is the v-slot:[expression], and the shorthand is the #[expression]. The expression must be the valid JavaScript, and the result must be the string. The directive argument is the dynamic, which is the Vue feature that allows the directive arguments to be the expressions. The v-bind:[attr] and the v-on:[event] are the other dynamic arguments, and the v-slot:[name] is the same mechanism for the slots.

This chapter covers three areas. First, why the dynamic slot names exist — the runtime-name problem and the data-driven rendering. Second, the syntax — the #[expression] in the parent, the :name="expression" in the child, and the scope of the expression. Third, the patterns and the pitfalls — the table columns, the form fields, the layout regions, the v-for with the dynamic names, and the reactivity of the names. The chapter ends with a complete example session, a quick reference, best practices, common pitfalls, real-world examples, and diagrams showing the dynamic slot flow.

Key point: A dynamic slot name is the slot name that is the JavaScript expression, not the fixed string. The parent uses the #[expression] syntax, and the child uses the :name="expression" syntax. The expression is evaluated in the scope where it appears, and the result must be the string. The dynamic name composes with the scoped slot props and the v-for, and it enables the data-driven rendering where the component and the parent agree on the names at the runtime.


Why the dynamic slot names exist

The runtime-name problem. The slot names are the fixed strings in the template. The <template #header> is the header slot, and the <template #footer> is the footer slot. But the component sometimes needs the slots that are not known until the runtime. The table component renders the columns from the columns prop, and the column names are the runtime data. The parent wants to provide the custom cell for the column named status, and the name is the data. The dynamic name lets the parent write the #[column.key] and the child write the :name="column.key", and the two match at the runtime.

The data-driven problem. The component renders the list from the data, and the parent wants to customize each item. The list component renders the ten items, and the parent wants the different rendering for the different types. The dynamic name lets the parent target the slot by the item type: the #[item-${item.type}] and the :name="item-${item.type}". The child and the parent agree on the naming convention, and the parent provides the different rendering for each type.

The configuration problem. The layout component renders the regions from the configuration, and the parent fills the region by the configured name. The regions are the data — the regions: ['header', 'main', 'sidebar', 'footer'] — and the parent wants to fill each region. The dynamic name lets the parent iterate the regions and provide the content for each.

The convention problem. The dynamic name is the string, and the component and the parent must agree on the convention. The slot-${type} is the convention, and the both sides use the same template literal. The convention is the contract, and the documentation is the requirement.

The expression-scope problem. The expression is evaluated in the scope where it appears. The parent’s expression is in the parent’s scope, and the child’s expression is in the child’s scope. The #[slotName] uses the parent’s slotName, and the :name="dynamicName" uses the child’s dynamicName. The two are the separate variables, and the string is the common value.

The trade-off. The dynamic name adds the indirection: the name is not the literal, and the reader must trace the expression. The wrong expression is the silent undefined slot, and the debugging is the harder. The trade-off is between the flexibility of the runtime names and the explicitness of the static names.


a. The dynamic slot name syntax

The parent uses the #[expression] syntax:

<template>
  <MyComponent>
    <template #[slotName]>
      <p>Dynamic content</p>
    </template>
  </MyComponent>
</template>

<script setup>
import { ref } from 'vue';

const slotName = ref('header');
</script>

The #[slotName] targets the slot named by the slotName value. When the slotName is the 'header', the content fills the header slot. When the slotName is the 'footer', the content fills the footer slot.

The v-slot long form is the same:

<template v-slot:[slotName]>
  <p>Dynamic content</p>
</template>

The child uses the :name binding:

<template>
  <div>
    <slot :name="dynamicName" />
  </div>
</template>

<script setup>
import { computed } from 'vue';

const props = defineProps({ type: String });
const dynamicName = computed(() => `slot-${props.type}`);
</script>

The :name is the dynamic, and the slot name is the slot-${type} value. The parent must use the same expression:

<template>
  <MyComponent :type="'error'">
    <template #[`slot-${'error'}`]>
      <p>Error content</p>
    </template>
  </MyComponent>
</template>

The parent’s #[slot-${‘error’}] and the child’s :name="slot-${type}" evaluate to the same slot-error, and the slot matches.

The v-for with the dynamic names. The parent can iterate the names and provide the content for each:

<template>
  <MyComponent>
    <template
      v-for="name in slotNames"
      #[name]
      :key="name"
    >
      <p>{{ name }} content</p>
    </template>
  </MyComponent>
</template>

<script setup>
const slotNames = ['header', 'main', 'footer'];
</script>

The v-for iterates the names, and the #[name] targets the slot for each. The :key is the requirement for the list rendering.

The scoped dynamic slot. The dynamic name composes with the scoped props:

<template>
  <DataTable :rows="rows" :columns="columns">
    <template
      v-for="col in columns"
      #[`cell-${col.key}`]="{ row, value }"
      :key="col.key"
    >
      <span>{{ value }}</span>
    </template>
  </DataTable>
</template>

The #[cell-${col.key}] targets the slot named cell-status, the cell-name, and the others. The child renders the slot with the matching name:

<template>
  <td v-for="col in columns" :key="col.key">
    <slot :name="`cell-${col.key}`" :row="row" :value="row[col.key]">
      {{ row[col.key] }}
    </slot>
  </td>
</template>

The :name="cell-${col.key}" matches the parent’s #[cell-${col.key}].


b. The patterns

The table columns. The table component renders the columns, and the parent provides the custom cell for the specific column.

<!-- DataTable.vue -->
<script setup>
defineProps({ rows: Array, columns: Array });
</script>

<template>
  <table>
    <tbody>
      <tr v-for="row in rows" :key="row.id">
        <td v-for="col in columns" :key="col.key">
          <slot
            :name="`cell-${col.key}`"
            :row="row"
            :value="row[col.key]"
            :column="col"
          >
            {{ row[col.key] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

The parent fills the specific columns:

<template>
  <DataTable :rows="rows" :columns="columns">
    <template #[`cell-status`]="{ value }">
      <StatusBadge :status="value" />
    </template>

    <template #[`cell-actions`]="{ row }">
      <button @click="edit(row)">Edit</button>
      <button @click="remove(row)">Delete</button>
    </template>
  </DataTable>
</template>

The cell-status and the cell-actions slots are the specific columns, and the other columns use the fallback.

The form fields. The form component renders the fields from the schema, and the parent provides the custom field rendering.

<!-- DynamicForm.vue -->
<script setup>
defineProps({ fields: Array });
</script>

<template>
  <form>
    <div v-for="field in fields" :key="field.name">
      <label>{{ field.label }}</label>
      <slot :name="`field-${field.name}`" :field="field">
        <input
          :type="field.type"
          :name="field.name"
          v-model="field.value"
        />
      </slot>
    </div>
  </form>
</template>

The parent fills the specific fields:

<template>
  <DynamicForm :fields="fields">
    <template #[`field-email`]="{ field }">
      <input type="email" :name="field.name" v-model="field.value" />
      <span v-if="field.error">{{ field.error }}</span>
    </template>
  </DynamicForm>
</template>

The layout regions. The layout component renders the regions, and the parent fills each.

<!-- Layout.vue -->
<script setup>
defineProps({ regions: Array });
</script>

<template>
  <div class="layout">
    <slot
      v-for="region in regions"
      :key="region"
      :name="`region-${region}`"
    />
  </div>
</template>

The parent fills the regions:

<template>
  <Layout :regions="['header', 'main', 'footer']">
    <template #[`region-header`]>
      <Nav />
    </template>
    <template #[`region-main`]>
      <Article />
    </template>
    <template #[`region-footer`]>
      <Footer />
    </template>
  </Layout>
</template>

The notification types. The component renders the notification, and the parent provides the different rendering for the different types.

<!-- Notification.vue -->
<script setup>
defineProps({ type: String, message: String });
</script>

<template>
  <div :class="`notification-${type}`">
    <slot :name="`notification-${type}`" :message="message">
      {{ message }}
    </slot>
  </div>
</template>

The parent fills the specific types:

<template>
  <Notification type="error" message="Something went wrong">
    <template #[`notification-error`]="{ message }">
      <strong>Error:</strong> {{ message }}
    </template>
  </Notification>
</template>

c. The pitfalls

The expression-scope pitfall. The parent’s expression and the child’s expression are evaluated in the separate scopes.

<!-- ❌ The wrong -->
<!-- The child -->
<script setup>
const type = 'error';
</script>
<slot :name="type" />

<!-- The parent -->
<template>
  <Child>
    <template #[type]>...</template>  <!-- the parent's type, not the child's -->
  </Child>
</template>

The type in the parent is the parent’s variable, and the type in the child is the child’s. They do not share, and the strings must match.

The reactivity pitfall. The dynamic name is reactive, and the slot content can change when the name changes.

<script setup>
const currentTab = ref('tab1');
</script>

<template>
  <Tabs>
    <template #[currentTab]>
      <TabContent :tab="currentTab" />
    </template>
  </Tabs>
</template>

The currentTab is the reactive, and the slot content changes when the currentTab changes.

The v-for with the dynamic names. The v-for with the dynamic slot name needs the :key:

<template
  v-for="name in names"
  #[name]
  :key="name"
>

The :key is the requirement, and the missing :key causes the warning.

The empty name pitfall. The dynamic name that evaluates to the empty string is the warning.

<!-- ❌ The empty name -->
<template #['']>
  <p>Content</p>
</template>

The slot name must be the non-empty string, and the empty name is the invalid.

The wrong type pitfall. The dynamic name must be the string.

<!-- ❌ The wrong type -->
<script setup>
const slotName = ref(123);
</script>

<template>
  <Child>
    <template #[slotName]>...</template>
  </Child>
</template>

The number is the coerced to the string '123', and the slot name is the '123'. The better is the explicit string.


Complete Example Session

<!-- ============================================
     PART 1: THE BASIC DYNAMIC NAME
     ============================================ -->

<!-- MyComponent.vue -->
<template>
  <div>
    <slot name="header" />
    <slot name="main" />
    <slot name="footer" />
  </div>
</template>

<!-- The parent -->
<script setup>
import { ref } from 'vue';
const slotName = ref('header');
</script>

<template>
  <MyComponent>
    <template #[slotName]>
      <p>Dynamic content</p>
    </template>
  </MyComponent>
</template>


<!-- ============================================
     PART 2: THE CHILD'S DYNAMIC NAME
     ============================================ -->

<!-- DynamicSlot.vue -->
<script setup>
defineProps({ type: String });
</script>

<template>
  <div>
    <slot :name="`slot-${type}`" />
  </div>
</template>

<!-- The parent -->
<template>
  <DynamicSlot type="error">
    <template #[`slot-error`]>
      <p>Error content</p>
    </template>
  </DynamicSlot>
</template>


<!-- ============================================
     PART 3: THE v-for WITH THE DYNAMIC NAMES
     ============================================ -->

<template>
  <MyComponent>
    <template
      v-for="name in ['header', 'main', 'footer']"
      #[name]
      :key="name"
    >
      <p>{{ name }} content</p>
    </template>
  </MyComponent>
</template>


<!-- ============================================
     PART 4: THE SCOPED DYNAMIC SLOT
     ============================================ -->

<template>
  <DataTable :rows="rows" :columns="columns">
    <template
      v-for="col in columns"
      #[`cell-${col.key}`]="{ row, value }"
      :key="col.key"
    >
      <span>{{ value }}</span>
    </template>
  </DataTable>
</template>


<!-- ============================================
     PART 5: THE TABLE COLUMNS
     ============================================ -->

<!-- DataTable.vue -->
<script setup>
defineProps({ rows: Array, columns: Array });
</script>

<template>
  <table>
    <tbody>
      <tr v-for="row in rows" :key="row.id">
        <td v-for="col in columns" :key="col.key">
          <slot
            :name="`cell-${col.key}`"
            :row="row"
            :value="row[col.key]"
            :column="col"
          >
            {{ row[col.key] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>


<!-- ============================================
     PART 6: THE FORM FIELDS
     ============================================ -->

<!-- DynamicForm.vue -->
<script setup>
defineProps({ fields: Array });
</script>

<template>
  <form>
    <div v-for="field in fields" :key="field.name">
      <label>{{ field.label }}</label>
      <slot :name="`field-${field.name}`" :field="field">
        <input :type="field.type" v-model="field.value" />
      </slot>
    </div>
  </form>
</template>


<!-- ============================================
     PART 7: THE LAYOUT REGIONS
     ============================================ -->

<!-- Layout.vue -->
<script setup>
defineProps({ regions: Array });
</script>

<template>
  <div class="layout">
    <slot
      v-for="region in regions"
      :key="region"
      :name="`region-${region}`"
    />
  </div>
</template>


<!-- ============================================
     PART 8: THE REACTIVITY
     ============================================ -->

<script setup>
import { ref, computed } from 'vue';

const currentTab = ref('tab1');
const slotName = computed(() => `tab-${currentTab.value}`);
</script>

<template>
  <Tabs>
    <template #[slotName]>
      <TabContent :tab="currentTab" />
    </template>
  </Tabs>
</template>


<!-- ============================================
     PART 9: THE EXPRESSION SCOPE PITFALL
     ============================================ -->

<!-- ❌ The wrong -->
<!-- The parent's type and the child's type are separate. -->
<Child>
  <template #[type]>...</template>
</Child>

<!-- ✅ The correct -->
<!-- Both sides use the same string. -->
<Child :type="type">
  <template #[`slot-${type}`]>...</template>
</Child>


<!-- ============================================
     PART 10: THE COMPLETE EXAMPLE
     ============================================ -->

<!-- DataTable.vue -->
<script setup>
defineProps({
  rows: { type: Array, required: true },
  columns: { type: Array, required: true },
});
</script>

<template>
  <table class="data-table">
    <thead>
      <tr>
        <th v-for="col in columns" :key="col.key">
          <slot :name="`header-${col.key}`" :column="col">
            {{ col.label }}
          </slot>
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in rows" :key="row.id">
        <td v-for="col in columns" :key="col.key">
          <slot
            :name="`cell-${col.key}`"
            :row="row"
            :column="col"
            :value="row[col.key]"
          >
            {{ row[col.key] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<!-- The parent -->
<script setup>
import { ref } from 'vue';

const rows = ref([
  { id: 1, name: 'Alice', status: 'active' },
  { id: 2, name: 'Bob', status: 'inactive' },
]);

const columns = ref([
  { key: 'name', label: 'Name' },
  { key: 'status', label: 'Status' },
  { key: 'actions', label: 'Actions' },
]);
</script>

<template>
  <DataTable :rows="rows" :columns="columns">
    <template #[`header-status`]="{ column }">
      <strong>{{ column.label }}</strong>
    </template>

    <template #[`cell-status`]="{ value }">
      <StatusBadge :status="value" />
    </template>

    <template #[`cell-actions`]="{ row }">
      <button @click="edit(row)">Edit</button>
      <button @click="remove(row)">Delete</button>
    </template>
  </DataTable>
</template>

The ten parts show the basic dynamic name, the child’s dynamic name, the v-for with the dynamic names, the scoped dynamic slot, the table columns, the form fields, the layout regions, the reactivity, the expression scope pitfall, and the complete example.


Quick Reference

The Syntax

The sideThe syntax
The parent#[expression]
The parent (the long form)v-slot:[expression]
The child:name="expression"
The child (the static)name="literal"

The Expression

The expressionThe result
#[slotName]The slotName value
#[slot-${type}]The template literal
#[name] in the v-forThe loop variable
#[currentTab]The reactive value

The Common Patterns

The patternThe childThe parent
The table:name="cell-${col.key}"#[cell-${col.key}]
The form:name="field-${field.name}"#[field-${field.name}]
The layout:name="region-${region}"#[region-${region}]
The notification:name="notification-${type}"#[notification-${type}]

The v-for Requirements

The requirementThe why
The :keyThe list rendering
The #[name]The dynamic name
The v-forThe iteration

The Pitfalls

The pitfallThe fix
The expression scopeUse the same string on both sides
The empty nameUse the non-empty string
The wrong typeUse the string
The missing :keyAdd the :key

Best Practices

✅ Do This:

<!-- Use the #[expression] in the parent -->
<template #[slotName]>...</template>                                      // ✅
<!-- Use the :name="expression" in the child -->
<slot :name="`slot-${type}`" />                                           // ✅
<!-- Use the v-for with the :key -->
<template v-for="name in names" #[name] :key="name">...</template>        // ✅
<!-- Use the template literal for the convention -->
#[`cell-${col.key}`]                                                      // ✅
<!-- Use the computed for the reactive name -->
const slotName = computed(() => `tab-${currentTab.value}`);               // ✅
<!-- Use the same string on both sides -->
<!-- The child: :name="`cell-${col.key}`" -->
<!-- The parent: #[`cell-${col.key}`] -->                                  // ✅

❌ Don’t Do This:

<!-- Don't use the child's variable in the parent's expression -->
<template #[childType]>...</template>  // the parent's scope                // ❌
<!-- Don't use the empty string -->
<template #['']>...</template>  // the invalid                             // ❌
<!-- Don't forget the :key in the v-for -->
<template v-for="name in names" #[name]>...</template>  // the missing key  // ❌
<!-- Don't use the number as the name -->
const slotName = ref(123);  // the coerced to '123'                         // ❌
<!-- Don't mix the convention -->
<!-- The child: :name="`cell-${col.key}`" -->
<!-- The parent: #[col.key]  // the missing prefix -->                       // ❌
<!-- Don't use the dynamic name when the static works -->
<template #[`header`]>...</template>  // the use #header                   // ✅

Common Pitfalls

The pitfallThe whyThe fix
The slot is not filledThe name mismatchCheck the string on both sides
The undefined slotThe wrong expressionCheck the scope
The empty nameThe empty stringUse the non-empty
The missing :keyThe v-forAdd the :key
The reactivity lostThe wrong bindingUse the computed
The child’s variable in the parentThe scopeUse the parent’s variable

Real-World Examples

1. The Basic Dynamic

<template #[slotName]>...</template>

2. The Child’s Dynamic

<slot :name="`slot-${type}`" />

3. The Table Cell

<template #[`cell-${col.key}`]="{ value }">{{ value }}</template>

4. The Form Field

<template #[`field-${field.name}`]="{ field }">...</template>

5. The Layout Region

<template #[`region-${region}`]>...</template>

6. The v-for

<template v-for="name in names" #[name] :key="name">...</template>

7. The Reactive Name

const slotName = computed(() => `tab-${currentTab.value}`);

8. The Scoped Dynamic

<template #[`cell-${col.key}`]="{ row, value }">...</template>

9. The Convention

<!-- Both sides use the same template literal -->

10. The Complete

<DataTable :rows="rows" :columns="columns">
  <template #[`cell-status`]="{ value }">
    <StatusBadge :status="value" />
  </template>
</DataTable>

Visual

The Dynamic Slot Flow

┌──────────────────────────────────────────────────────────────┐
│  THE DYNAMIC SLOT FLOW                                       │
│                                                              │
│  The child:                                                  │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <td v-for="col in columns">                         │    │
│  │    <slot :name="`cell-${col.key}`" :value="..." />   │    │
│  │  </td>                                               │    │
│  │                                                       │    │
│  │  The child renders the slot named cell-name,         │    │
│  │  cell-status, cell-actions, ...                      │    │
│  └──────────────────────────────────────────────────────┘    │
│    │                                                         │
│    │  the dynamic names                                     │
│    ▼                                                         │
│  The parent:                                                 │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <DataTable>                                         │    │
│  │    <template #[`cell-status`]="{ value }">           │    │
│  │      <StatusBadge :status="value" />                 │    │
│  │    </template>                                       │    │
│  │  </DataTable>                                        │    │
│  │                                                       │    │
│  │  The parent fills the cell-status slot.              │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Expression Scope

┌──────────────────────────────────────────────────────────────┐
│  THE EXPRESSION SCOPE                                        │
│                                                              │
│  The child's scope:                                          │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <script setup>                                      │    │
│  │  const type = 'error';                               │    │
│  │  </script>                                           │    │
│  │                                                       │    │
│  │  <slot :name="`slot-${type}`" />                     │    │
│  │    → the 'slot-error'                                │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The parent's scope:                                         │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <script setup>                                      │    │
│  │  const type = 'error';  // the PARENT's variable     │    │
│  │  </script>                                           │    │
│  │                                                       │    │
│  │  <Child>                                             │    │
│  │    <template #[`slot-${type}`]>...</template>        │    │
│  │    → the 'slot-error'                                │    │
│  │  </Child>                                            │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The two variables are the separate. The strings must        │
│  match, but the variables do not.                            │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The v-for with the Dynamic Names

┌──────────────────────────────────────────────────────────────┐
│  THE v-for WITH THE DYNAMIC NAMES                            │
│                                                              │
│  <template>                                                  │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <MyComponent>                                       │    │
│  │    <template                                         │    │
│  │      v-for="name in ['header', 'main', 'footer']"    │    │
│  │      #[name]                                         │    │
│  │      :key="name"                                     │    │
│  │    >                                                 │    │
│  │      <p>{{ name }} content</p>                       │    │
│  │    </template>                                       │    │
│  │  </MyComponent>                                      │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The v-for iterates the names, and the #[name] targets       │
│  the slot for each. The :key is the requirement.             │
│                                                              │
└──────────────────────────────────────────────────────────────┘

The Table Columns

┌──────────────────────────────────────────────────────────────┐
│  THE TABLE COLUMNS                                           │
│                                                              │
│  The child:                                                  │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <td v-for="col in columns">                         │    │
│  │    <slot :name="`cell-${col.key}`" :value="...">     │    │
│  │      {{ row[col.key] }}  ← the fallback              │    │
│  │    </slot>                                           │    │
│  │  </td>                                               │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The parent:                                                 │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  <template #[`cell-status`]="{ value }">             │    │
│  │    <StatusBadge :status="value" />                   │    │
│  │  </template>                                         │    │
│  │                                                       │    │
│  │  <template #[`cell-actions`]="{ row }">              │    │
│  │    <button @click="edit(row)">Edit</button>          │    │
│  │  </template>                                         │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                              │
│  The parent fills the specific columns, and the others       │
│  use the fallback.                                           │
│                                                              │
└──────────────────────────────────────────────────────────────┘

Summary

ItemValue
The dynamic nameThe slot name that is the expression
The parent#[expression]
The parent (the long)v-slot:[expression]
The child:name="expression"
The scopeThe expression in the local scope
The stringThe contract between the two
The v-forThe :key is the requirement
The reactiveThe computed for the changing names

Key takeaways:

  • A dynamic slot name is the slot name that is the JavaScript expression, not the fixed string. The parent uses the #[expression] syntax, and the child uses the :name="expression" syntax. The two must evaluate to the same string.
  • The expression is evaluated in the scope where it appears. The parent’s expression is in the parent’s scope, and the child’s expression is in the child’s scope. The variables are the separate, and the string is the contract.
  • The dynamic name composes with the scoped slot props. The #[cell-${col.key}]="{ value }" combines the dynamic name and the scoped props.
  • The v-for with the dynamic names needs the :key. The v-for="name in names" #[name] :key="name" iterates the names and targets the slot for each.
  • The dynamic name is reactive. The computed provides the changing name, and the slot content updates when the name changes.
  • The convention is the contract. The child and the parent must agree on the naming convention. The slot-${type} is the convention, and the both sides use the same template literal.
  • The static names are the simpler. The dynamic name is for the runtime names, and the static is for the known. The static is the explicit and the debuggable.
  • The result is the data-driven rendering. The component and the parent agree on the names at the runtime, and the parent provides the rendering for the specific data. The dynamic name is the foundation of the table columns, the form fields, and the layout regions.

Remember: A dynamic slot name is the slot name that is the expression. The parent uses the #[expression], and the child uses the :name="expression". The string is the contract, and the convention is the agreement. The result is the data-driven rendering, which is the foundation of the reusable table, the dynamic form, and the flexible layout.



Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!