Reusable Data with definitions

As you build templates, you'll notice you're defining the same group of questions over and over. For example, a "person" might always have a name, email, and phone number. A "company" always has a legal name, registration number, and address.

The definitions block in config.yml lets you define these reusable data structures once, and then use them throughout your project.

Definitions pair well with components. If you want reusable presentation plus reusable data, see Building with Components after this guide.

Creating Your First Definition

In config.yml, add a top-level block called definitions. Inside it, you can define your reusable structures.

# config.yml

definitions:
  # A simple definition for contact information
  contact_definition:
    full_name:
      label: Full Name
      type: text
      help: Enter the person's complete name
    email:
      label: Email Address
      type: email
      required: false
    phone:
      label: Phone Number
      type: text
      required: false

  # A simple definition for address information
  address_definition:
    street:
      label: Street Address
      type: text
    city:
      label: City
      type: text
    country:
      label: Country
      type: select
      options:
        - USA
        - Canada
        - UK
        - Australia

Composing Complex Definitions

Definitions can reference other definitions, creating powerful composition patterns:

definitions:
  # Build on the basic contact definition
  employee_definition:
    # This field uses the contact_definition
    personal_info:
      label: Personal Information
      type: structured
      definition: contact_definition

    # Additional fields specific to employees
    employee_id:
      label: Employee ID
      type: text
    department:
      label: Department
      type: select
      options:
        - Engineering
        - Sales
        - Marketing
        - HR

    # Work address using the address definition
    work_address:
      label: Work Address
      type: structured
      definition: address_definition
      required: false

    # List of projects (references another definition)
    projects:
      label: Assigned Projects
      type: list
      item_label: Project
      uses: project_definition
      required: false

  project_definition:
    name:
      label: Project Name
      type: text
    description:
      label: Description
      type: textarea
      required: false
    budget:
      label: Budget
      type: number
      min: 0    
      required: false

Notice how employee_definition is composed of simple types and references to other definitions (contact_definition, address_definition, project_definition). This is the key to building powerful, maintainable data structures.

Recursive Definition Resolution

VIBE automatically resolves nested definition references and detects circular dependencies:

# This would cause an error:
definitions:
  a_definition:
    b_field:
      type: structured
      definition: b_definition  # References b_definition

  b_definition:
    a_field:
      type: structured
      definition: a_definition  # References a_definition - CIRCULAR!

VIBE will detect this and show a clear error:

Circular definition reference detected: a_definition -> b_definition -> a_definition

Using Definitions in Questions

Now you can use these definitions in your questions block.

For a single structured object (type: structured)

This is for when you need to ask for one person's details.

questions:
  primary_contact:
    label: Primary Contact Person
    type: structured
    definition: contact_definition
    help: Main person responsible for this project

  project_manager:
    label: Project Manager
    type: structured
    definition: employee_definition

VIBE will automatically generate all the necessary fields, including nested structures.

For a list of objects (type: list)

This is for when you need to ask for a list of multiple items.

questions:
  team_members:
    label: Team Members
    type: list
    item_label: Team Member
    uses: contact_definition
    min_items: 1
    max_items: 10

  departments:
    label: Company Departments
    type: list
    item_label: Department
    uses: employee_definition
    required: false

Template Access Patterns

Access definition-based data in templates using dot notation:

# Primary Contact
**Name:** {{ primary_contact.full_name }}
**Email:** {{ primary_contact.email }}
**Phone:** {{ primary_contact.phone }}

# Project Manager (nested structure)
**Manager:** {{ project_manager.personal_info.full_name }}
**Department:** {{ project_manager.department }}
**Employee ID:** {{ project_manager.employee_id }}
**Work Address:** {{ project_manager.work_address.street }}, {{ project_manager.work_address.city }}

# Team Members (list)
{% for member in team_members %}
- **{{ member.full_name }}** ({{ member.email }})
{% endfor %}

# Project assignments (nested list)
{% for project in project_manager.projects %}
- **{{ project.name }}**: {{ project.description }}
  Budget: ${{ project.budget }}
{% endfor %}

Linking Components to Definitions

This is where the system's power becomes clear. You can create a file-based component and declare that it operates on a specific data definition. This formalizes the "data contract" for your component.

Basic Component-Definition Linking:

components/contact_card/component.yml

# This component inherits all fields from contact_definition
uses: contact_definition

# The component automatically gets these inputs:
# - full_name
# - email  
# - phone

components/contact_card/template.md

<div class="contact-card">
  <h4>{{ full_name }}</h4>
  <p>Email: {{ email }}</p>
  {% if phone %}<p>Phone: {{ phone }}</p>{% endif %}
</div>

Extending Definitions in Components:

Components can add additional fields to a definition:

# components/employee_card/component.yml
uses: contact_definition

# Add fields beyond the base definition
inputs:
  department:
    label: Department
    type: select
    options:
      - Engineering
      - Sales
      - Marketing

  hire_date:
    label: Hire Date
    type: date
    required: false

# Component-specific questions
questions:
  show_hire_date:
    label: Show hire date on card?
    type: bool

The component now has all contact_definition fields PLUS department and hire_date.

Host Template Integration:

When you use a component that links to a definition, the host template gets namespaced questions:

# {{ company_name }}

## Employee Information
{{ insert('employee_card', alias='emp_info') }}

Resulting questions in host template:

  • company_name (from host template)
  • emp_info.full_name (from contact_definition via employee_card component)
  • emp_info.email (from contact_definition via employee_card component)
  • emp_info.phone (from contact_definition via employee_card component)
  • emp_info.department (from employee_card component inputs)
  • emp_info.hire_date (from employee_card component inputs)
  • emp_info.show_hire_date (from employee_card component questions)

Form Data Processing

The system automatically handles form data mapping:

  1. Browser form field names: emp_info.full_name, emp_info.email
  2. Session data storage: { "emp_info": { "full_name": "John Doe", "email": "john@example.com" } }
  3. Component context: { "full_name": "John Doe", "email": "john@example.com" }

Conflict Detection and Resolution

Components cannot override definition fields with conflicting types:

# This would cause an error:
uses: contact_definition

inputs:
  full_name:  # ERROR: Conflicts with contact_definition.full_name
    label: Different Name Label
    type: number  # Conflicts with text type in definition
    required: false

VIBE generates a clear error:

Input 'full_name' conflicts with definition field: definition has type 'text', input has type 'number'

Valid extensions are allowed:

# This is OK - adds new fields or extends compatibly
uses: contact_definition

inputs:
  # This is OK - adds a new field
  employee_id:
    label: Employee ID
    type: text

  # This is OK - same type, compatible constraints
  full_name:
    label: Full Name (Employee)
    type: text
    help: Employee's full legal name

Advanced Definition Patterns

Multi-Level Composition:

definitions:
  # Base contact information
  contact_definition:
    name:
      label: Name
      type: text
    email:
      label: Email
      type: email
      required: false

  # Employee extends contact
  employee_definition:
    personal_info:
      label: Personal Information
      type: structured
      definition: contact_definition
    employee_id:
      label: Employee ID
      type: text
    department:
      label: Department
      type: text

  # Manager extends employee
  manager_definition:
    employee_info:
      label: Employee Information
      type: structured
      definition: employee_definition
    team_size:
      label: Team Size
      type: number
      min: 1
    budget_authority:
      label: Budget Authority
      type: number
      min: 0
      required: false

Component Variants:

Create multiple component variants for different use cases:

# components/basic_contact/component.yml
uses: contact_definition
inputs: {}

# components/employee_contact/component.yml  
uses: contact_definition
inputs:
  department:
    label: Department
    type: text

# components/manager_contact/component.yml
uses: contact_definition
inputs:
  department:
    label: Department
    type: text
  team_size:
    label: Team Size
    type: number
    min: 1

Best Practices for Definitions

Definition Design:

  1. Single Responsibility: Each definition should represent a cohesive data structure
  2. Reusability: Design definitions for maximum reuse across different contexts
  3. Semantic Naming: Use clear, descriptive names ending with _definition
  4. Logical Grouping: Group related fields together within definitions

Template Organization:

  1. Definition First: Define your data structures before creating questions
  2. Logical Ordering: Place definitions at the top of config.yml
  3. Consistent Usage: Use the same definition across related components
  4. Clear Separation: Keep definitions, questions, and component configuration distinct

Migration Path:

If you have existing templates with repeated field definitions:

Before (Repetitive):

questions:
  manager:
    type: structured
    fields:
      name:
        type: text
      email:
        type: email
        required: false

  contact:
    type: structured  
    fields:
      name:
        type: text
      email:
        type: email
        required: false

After (Definition-based):

definitions:
  person_definition:
    name:
      type: text
    email:
      type: email
      required: false

questions:
  manager:
    type: structured
    definition: person_definition

  contact:
    type: structured
    definition: person_definition

This approach provides better maintainability, consistency, and enables powerful component integration patterns.