# How Slot-Instance and Slot-Outlet Pairing Works in Instatic Visual Components

> Understand slot-instance and slot-outlet pairing in Instatic Visual Components. Learn how matching slotName property materializes container nodes for content injection during rendering.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: internals
- Published: 2026-07-26

---

**In Instatic, **slot-instance** and **slot-outlet** nodes are paired by matching the **`slotName`** property, allowing Visual Component references to automatically materialize container nodes that inject content into designated placeholder positions during rendering.**

The **CoreBunch/Instatic** repository implements a declarative slot system where Visual Components (VCs) define injection points using `base.slot-outlet` nodes. When a VC is instantiated on a page via a `base.visual-component-ref`, the framework ensures a corresponding `base.slot-instance` exists for every declared outlet, maintaining synchronization through a deterministic naming contract.

## The Architecture of Slot Pairing

Visual Components in Instatic use a strict separation between template definitions and content consumption. The **slot-instance and slot-outlet pairing** relies on a shared identifier that bridges the VC definition and its runtime usage.

### Slot Outlets: The Template Side

A **slot-outlet** acts as a placeholder marker inside the VC tree. According to the source in [`src/modules/base/slotOutlet/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotOutlet/index.ts), each outlet node declares a `slotName` property that identifies where content should appear.

```typescript
// src/modules/base/slotOutlet/index.ts
// Module definition declaring the slotName prop
{
  moduleId: 'base.slot-outlet',
  props: {
    slotName: 'header' // Unique identifier for this injection point
  }
}

```

### Slot Instances: The Consumer Side

When a user drops a Visual Component onto a page, the editor creates a `base.visual-component-ref` node. The framework automatically materializes **slot-instance** children under this reference, as defined in [`src/modules/base/slotInstance/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotInstance/index.ts). Each instance carries a `props.slotName` that must match an outlet's `slotName` to establish the connection.

## The Synchronization Algorithm

The `syncSlotInstances` function in [`src/core/visualComponents/slotSync.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts) maintains the relationship between outlets and instances. This pure algorithm computes the minimal set of tree mutations required to keep the VC reference synchronized with its definition.

### Collecting Outlet Definitions

The process begins with `collectSlotOutletNames`, which traverses the VC tree using a **DFS pre-order traversal** (first appearance wins). This generates an ordered list of slot names that defines the expected child structure.

### Matching and Reconciliation

The algorithm performs three operations to reconcile the existing slot-instance children with the required outlets:

1. **Insertion**: Creates new `base.slot-instance` nodes for outlets that lack corresponding instances.
2. **Renaming**: Adjusts `slotName` properties when instances exist but names differ, matching first by name then by position.
3. **Deletion**: Removes stray slot-instance children that no longer map to declared outlets.

The result is an ordered list of child IDs that mirrors the slot-outlet declaration order, applied via `applySlotSyncResult`.

```typescript
// src/core/visualComponents/slotSync.ts
import { syncSlotInstances, applySlotSyncResult } from '@core/visualComponents/slotSync'

const result = syncSlotInstances(vcRefNode, visualComponentDefinition, pageNodes)
applySlotSyncResult(pageNodes, result, vcRefNode.id)

```

## Rendering and Content Injection

During the publish phase, the renderer processes the `base.visual-component-ref` by executing `renderVisualComponentRef` in [`src/core/publisher/renderVisualComponentRef.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderVisualComponentRef.ts).

### Transparent Substitution

Because `base.slot-outlet` nodes have **transparent publish behavior**, they emit no HTML of their own. Instead, the renderer substitutes the subtree of each matching `base.slot-instance` into the outlet's position. This means the published output contains only the injected content, not the placeholder wrappers.

### Shared Content Across Multiple Outlets

If a Visual Component declares multiple `base.slot-outlet` nodes with the identical `slotName`, they all render the **same** slot-instance content. This allows a single consumer node to populate several template positions simultaneously.

## Practical Implementation Example

The following workflow demonstrates the complete lifecycle from VC definition to rendered output.

### 1. Define a Visual Component with Slots

```typescript
// Define a VC with two distinct slot-outlets
export const MyCardVC = {
  moduleId: 'my-extensions.card',
  tree: {
    rootNodeId: 'root',
    nodes: {
      root: { 
        id: 'root', 
        moduleId: 'base.container', 
        children: ['header', 'body'] 
      },
      header: { 
        id: 'header', 
        moduleId: 'base.slot-outlet', 
        props: { slotName: 'header' }, 
        children: [] 
      },
      body: { 
        id: 'body', 
        moduleId: 'base.slot-outlet', 
        props: { slotName: 'body' }, 
        children: [] 
      },
    },
  },
}

```

### 2. Synchronize on Instantiation

When the editor adds this VC to a page, it calls the synchronization routine to materialize the slot instances:

```typescript
import { syncSlotInstances, applySlotSyncResult } from '@core/visualComponents/slotSync'

// vcRefNode is the new base.visual-component-ref in the page tree
const syncResult = syncSlotInstances(vcRefNode, MyCardVC, page.nodes)

// Apply the mutative recipe: adds, renames, deletes, and reorders children
applySlotSyncResult(page.nodes, syncResult, vcRefNode.id)

```

### 3. Publish with Content Injection

During rendering, the publisher walks the VC reference and injects user-created content (placed under the slot-instance nodes) into the matching outlet positions:

```typescript
// In the render pipeline (src/core/publisher/renderVisualComponentRef.ts)
renderVisualComponentRef(vcRefNode, MyCardVC, renderContext)
// Walks each base.slot-instance child (e.g., Text blocks, Images)
// Injects their rendered output into every matching base.slot-outlet position

```

## Summary

- **slot-instance and slot-outlet pairing** depends entirely on the `slotName` property matching between the template declaration and the materialized instance.
- The `syncSlotInstances` algorithm in [`src/core/visualComponents/slotSync.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts) computes minimal tree mutations to maintain this pairing using DFS-ordered outlet collection.
- `base.slot-outlet` nodes are transparent during publishing and emit no HTML, serving only as injection markers.
- Multiple outlets sharing a `slotName` render the identical slot-instance content at each position.
- Key source files include [`src/modules/base/slotOutlet/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotOutlet/index.ts), [`src/modules/base/slotInstance/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotInstance/index.ts), and [`src/core/publisher/renderVisualComponentRef.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderVisualComponentRef.ts).

## Frequently Asked Questions

### What is the role of the slotName property in Instatic?

The `slotName` property acts as the deterministic key that links a `base.slot-outlet` declaration in a Visual Component template to a `base.slot-instance` consumer node on the page. Both sides must share the identical string value for the pairing to succeed, as implemented in the synchronization logic within [`src/core/visualComponents/slotSync.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts).

### How does Instatic handle slot synchronization when a Visual Component is updated?

When a VC definition changes, the system re-runs `syncSlotInstances` against existing page references. The algorithm collects the new set of outlet names via `collectSlotOutletNames`, then inserts missing instances, renames mismatched ones based on position, and deletes orphaned children, ensuring the page tree remains valid without manual intervention.

### Can multiple slot-outlets share the same slotName?

Yes. When a Visual Component contains multiple `base.slot-outlet` nodes with identical `slotName` values, the publisher injects the single corresponding `base.slot-instance` content into every matching position. This allows content authors to populate multiple template areas simultaneously from one content source.

### Where is the slot pairing logic defined in the CoreBunch/Instatic repository?

The core pairing algorithm resides in [`src/core/visualComponents/slotSync.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts), specifically within the `syncSlotInstances` function. Module definitions for the nodes themselves are located at [`src/modules/base/slotOutlet/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotOutlet/index.ts) and [`src/modules/base/slotInstance/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotInstance/index.ts), while the rendering substitution logic is implemented in [`src/core/publisher/renderVisualComponentRef.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderVisualComponentRef.ts).