# How Instatic Visual Components Work: A Complete Guide to Typed Parameters and Named Slots

> Explore Instatic Visual Components. Learn how typed parameters and named slots create reusable UI blocks for dynamic content injection. Understand the synchronization algorithm.

- Repository: [CoreBunch/Instatic](https://github.com/CoreBunch/Instatic)
- Tags: deep-dive
- Published: 2026-07-30

---

**Instatic Visual Components combine strict TypeBox schemas for parameters with a synchronization algorithm that automatically manages slot-outlet pairs, enabling reusable UI blocks where content is injected into placeholder positions defined by the component author.**

Instatic is an open-source static site builder that treats UI components as first-class entities within the page tree. Understanding how **Instatic Visual Components** handle typed parameters and content slots is essential for building reusable, maintainable templates that stay synchronized between the editor and the published output.

## Typed Parameters in Visual Components

Every Visual Component (VC) declares its configurable interface through a structured parameter schema. These definitions live in the `site.visualComponents` array and enforce type safety at both edit-time and publish-time.

### Parameter Schema Definition

Parameters are defined using the `VCParam` schema located in [[`src/core/visualComponents/schemas.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/schemas.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/schemas.ts). Each parameter requires a stable `id`, human-readable `name`, and a `type` from the `VCParamTypeSchema` enumeration.

The supported types include:

- `string`, `number`, `boolean` – Primitive values
- `url`, `color`, `image` – Specialized string formats
- `enum` – Constrained selection with `enumOptions` array
- `richText` – Formatted content blocks
- `slot` – **Named content insertion points**

A complete parameter definition also includes optional `description`, a `defaultValue`, a boolean `required` flag, and conditional fields like `enumOptions` for enumerated types. The runtime validates incoming values against this schema using TypeBox before the component logic executes.

### Validation and Fallback Behavior

The parsing logic in [`src/core/visualComponents/schemas.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/schemas.ts) implements defensive defaults. Unknown parameter types gracefully fall back to `string`, missing fields receive sensible empty values, and malformed entries are filtered out without corrupting the entire component definition. This tolerance ensures that version mismatches between saved content and code updates don't break existing pages.

## The Slot Mechanism: Outlets and Instances

Slots enable Visual Components to expose placeholder positions where page authors can inject custom content. The architecture separates the **declaration** (where content should go) from the **materialization** (what content actually goes there).

### Declaring Slot Outlets in VC Definitions

Inside a Visual Component's `NodeTree<VCNode>`, authors place **slot outlet** nodes using the module ID `base.slot-outlet`. Defined in [[`src/modules/base/slotOutlet/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotOutlet/index.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotOutlet/index.ts), these nodes act as markers with a `slotName` property (defaulting to `"children"`) that identifies the specific insertion point.

The slot outlet module's `render` method returns empty HTML; it serves purely as a coordination marker for the publisher. During static site generation, the walker detects these outlets and substitutes them with content from the corresponding slot instance.

### Materializing Slots with Slot Instances

When a page author references a Visual Component via `base.visualComponentRef`, the system automatically creates **locked** child nodes of type `base.slot-instance` for every slot parameter declared in the VC's `params` array. This materialization logic resides in [[`src/modules/base/slotInstance/index.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotInstance/index.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/modules/base/slotInstance/index.ts).

Key characteristics of slot instances:

- **Locked state**: Users cannot delete or reorder the `slot-instance` node itself, preventing accidental destruction of the slot structure
- **Editable children**: The children inside a slot instance (the actual content nodes) remain fully editable
- **Transparent publishing**: The slot instance module sets `publishBehavior` to `"transparent"` and returns empty HTML, ensuring only the content children render

### The Slot Synchronization Algorithm

The `syncSlotInstances` function in [[`src/core/visualComponents/slotSync.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/core/visualComponents/slotSync.ts) maintains consistency between VC definitions and their page references. The algorithm performs the following steps:

1. Walks the VC's node tree to collect all unique `slotName` values from `base.slot-outlet` nodes
2. Compares the discovered slots against existing `base.slot-instance` children in the page reference
3. Generates operations to **insert** missing slots, **rename** changed slots, or **delete** orphaned instances

This synchronization guarantees that adding, removing, or renaming a slot in the Visual Component definition automatically updates every existing reference across the site without manual user intervention.

### Publishing and Render Behavior

During static site rendering, the publisher's walker encounters a `base.slot-outlet` node and performs a lookup in [[`src/core/publisher/renderVisualComponentRef.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderVisualComponentRef.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/core/publisher/renderVisualComponentRef.ts). It finds the matching `base.slot-instance` child by `slotName` and substitutes the outlet position with the instance's children.

If a slot contains no content and lacks a `defaultValue`, the outlet emits empty HTML as a safety net, preventing rendering errors in the final output.

## Visual Component References on Pages

Pages embed Visual Components using the `base.visualComponentRef` module. This reference node stores the target `visualComponentId` and, through the slot-sync routine, automatically gains the appropriate locked `slot-instance` children.

The ref's renderer extracts the VC's complete node tree, merges the consumer's slot content at the outlet positions, and produces the final HTML/CSS. Site selectors in [[`src/core/page-tree/siteSelectors.ts`](https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteSelectors.ts)](https://github.com/CoreBunch/Instatic/blob/main/src/core/page-tree/siteSelectors.ts) handle the resolution of VC definitions from the site-wide registry.

## Complete Workflow Example

The following examples demonstrate the full lifecycle from VC definition to published output.

**1. Visual Component Definition**

```typescript
// src/core/visualComponents/schemas.ts (conceptual usage)
export const CardVC = {
  id: 'vc-card',
  name: 'Card',
  tree: {
    rootNodeId: 'root',
    nodes: {
      root: { 
        moduleId: 'base.container', 
        children: ['header', 'content'] 
      },
      header: { 
        moduleId: 'base.text', 
        props: { text: 'Card Header' } 
      },
      content: { 
        moduleId: 'base.slot-outlet', 
        props: { slotName: 'body' } 
      },
    },
  },
  params: [
    { 
      id: 'p1', 
      name: 'title', 
      type: 'string', 
      defaultValue: 'Untitled' 
    },
    { 
      id: 'p2', 
      name: 'body', 
      type: 'slot' 
    },
  ],
  classIds: [],
  createdAt: Date.now(),
}

```

**2. Page Reference with Auto-Generated Slot Instance**

```typescript
// Page node tree structure
{
  id: 'ref-123',
  moduleId: 'base.visualComponentRef',
  props: { visualComponentId: 'vc-card' },
  children: [
    {
      id: 'slot-inst-body',
      moduleId: 'base.slot-instance',
      props: { slotName: 'body' },
      // This node is locked; children below are editable
      children: [
        {
          id: 'text-1',
          moduleId: 'base.text',
          props: { text: 'Hello from the slot!' }
        }
      ]
    }
  ]
}

```

**3. Publisher Resolution**

When the publisher processes the `base.slot-outlet` with `slotName: 'body'`, it retrieves the children from the matching `base.slot-instance` and renders them at that position in the output HTML.

## Summary

Instatic's Visual Component system provides a robust, type-safe approach to reusable UI construction:

- **Schema-driven parameters** using `VCParam` definitions enforce type safety across `string`, `number`, `boolean`, `enum`, `color`, `image`, `richText`, and `slot` types
- **Automatic slot synchronization** via `syncSlotInstances` ensures page references stay aligned with VC definitions
- **Separation of concerns** between `base.slot-outlet` (declaration) and `base.slot-instance` (materialization) enables safe content injection
- **Locked tree structures** prevent users from accidentally breaking component contracts while preserving content editing flexibility

## Frequently Asked Questions

### What happens when a new slot parameter is added to an existing Visual Component?

When you add a new `type: 'slot'` entry to a VC's `params` array, the `syncSlotInstances` algorithm detects the change during the next editor session or site build. It automatically inserts a new locked `base.slot-instance` child node into every existing `base.visualComponentRef` on the site. The slot starts empty (or uses the `defaultValue` if provided) and awaits content from page authors.

### How does Instatic handle invalid parameter values in Visual Components?

The runtime validates all incoming props against the `VCParam` schema before component execution. If a parameter contains an unknown type, the system falls back to `string`. Missing fields receive sensible defaults defined in the schema, and completely malformed entries are dropped silently. This defensive parsing prevents individual parameter errors from crashing the entire component or page.

### Can a single Visual Component expose multiple named slots?

Yes. You can declare multiple parameters with `type: 'slot'` and distinct names like `"header"`, `"sidebar"`, and `"footer"`. Inside the VC's `NodeTree`, you place corresponding `base.slot-outlet` nodes with matching `slotName` properties. The slot-sync routine creates a separate `base.slot-instance` child for each slot parameter, allowing page authors to populate each area with independent content.

### Why are slot-instance nodes locked in the page tree?

`base.slot-instance` nodes are locked to preserve the structural contract between the Visual Component definition and its usage. This prevents authors from accidentally deleting the slot container or reordering it outside the component's expected hierarchy. However, the children inside the slot instance—where the actual content lives—remain fully editable, providing the necessary flexibility while maintaining architectural integrity.