Component Registration
Define reusable components that can be used throughout your application with a simple registration API.
Component Workflow
Register Once
Define component with template, data, and methods
Use Anywhere
Insert with <component> tag throughout your app
Independent Instances
Each component has its own reactive state
// Register a reusable counter component
XTool.registerComponent({
  name: 'counter',
  template: `<div class="counter">
    <h3>Count: \{{ count }}</h3>
    <button x-on:click="increment">
      +1
    </button>
    <button x-on:click="reset">
      Reset
    </button>
  </div>`,
  
  makeData: (props) => ({
    count: Number(props.start || 0)
  }),
  
  methods: {
    increment() {
      this.count++;
    },
    reset() {
      this.count = 0;
    }
  }
});<!-- Use the component multiple times -->
<div x-data="{ message: 'Hello Components!' }">
  <h1 x-text="message"></h1>
  
  <!-- Counter starting at 0 -->
  <component source="counter"></component>
  
  <!-- Counter starting at 10 -->
  <component source="counter" 
             x-prop="{ start: 10 }">
  </component>
  
  <!-- Counter starting at 100 -->
  <component source="counter" 
             x-prop="{ start: 100 }">
  </component>
</div>Dynamic Component Switching
Swap components on the fly by binding the source attribute—just like changing an image src.
<div x-data="{ selected: 'mini-counter' }">
  <select x-model="selected">
    <option value="mini-counter">Counter</option>
    <option value="mini-quote">Quote</option>
  </select>
  <component x:source="selected"></component>
</div>- Supports lazy loading via XTool.loadComponents({ mode: 'lazy' }).
- Keeps instances isolated; switching remounts the chosen component.
- Great for tabs, previewers, and dashboards.
Readonly Freeze Mode
Freeze a component to disable state changes and renders—perfect for previews, embeds, and audits.
Attribute & Binding
<!-- Static freeze -->
<component source="stats-card" readonly></component>
<!-- Reactive toggle -->
<component source="mini-counter" x-bind:readonly="isFrozen"></component>Behavior
- Blocks updates and event effects; state is preserved while frozen.
- Remove readonly to resume interactivity seamlessly.
- Pairs nicely with dynamic switching for safe previews.
Lifecycle Hooks
Control component behavior at key moments with 7 lifecycle hooks for complete lifecycle management.
Mount Phase
beforeMount
                  Setup before DOM
mounted
                  Component ready
Update Phase
updated
                  Data changed
Unmount Phase
beforeUnmount
                  Cleanup prep
unmounted
                  Removed from DOM
Destroy Phase
beforeDestroy
                  Final cleanup
destroyed
                  Fully destroyed
Lifecycle Execution Flow
Interactive Examples
Timer Component
Basic lifecycle with interval management
Data Fetcher
Advanced async operations with cleanup
Complete Reference
All 7 lifecycle hooks demonstrated
// Timer component with lifecycle management
XTool.registerComponent({
  name: 'timer',
  template: `<div>
    <p>Seconds: \{{ seconds }}</p>
    <button x-on:click="toggle">
      \{{ isRunning ? 'Pause' : 'Start' }}
    </button>
  </div>`,
  
  makeData: () => ({
    seconds: 0,
    interval: null,
    isRunning: false
  }),
  
  beforeMount() {
    console.log('Timer initializing...');
  },
  
  mounted() {
    console.log('Timer ready!');
    this.start();
  },
  
  updated() {
    console.log('Timer updated:', this.seconds);
  },
  
  beforeUnmount() {
    this.stop();
    console.log('Timer cleanup complete');
  },
  
  methods: {
    start() {
      if (!this.interval) {
        this.interval = setInterval(() => {
          this.seconds++;
        }, 1000);
        this.isRunning = true;
      }
    },
    stop() {
      if (this.interval) {
        clearInterval(this.interval);
        this.interval = null;
        this.isRunning = false;
      }
    },
    toggle() {
      this.isRunning ? this.stop() : this.start();
    }
  }
});// Data fetcher with complete lifecycle
XTool.registerComponent({
  name: 'data-fetcher',
  template: `<div>
    <div x-show="loading">Loading...</div>
    <div x-show="error" x-text="error"></div>
    <div x-show="data" x-text="data"></div>
  </div>`,
  
  makeData: (props) => ({
    data: null,
    loading: false,
    error: null,
    controller: null
  }),
  
  beforeMount() {
    // Initialize AbortController
    this.controller = new AbortController();
  },
  
  mounted() {
    // Start data fetching
    this.fetchData();
  },
  
  updated() {
    // Log state changes
    if (this.error) console.error('Fetch error:', this.error);
    if (this.data) console.log('Data received:', this.data);
  },
  
  beforeUnmount() {
    // Cancel any pending requests
    if (this.controller) {
      this.controller.abort();
    }
  },
  
  unmounted() {
    console.log('Data fetcher cleaned up');
  },
  
  methods: {
    async fetchData() {
      this.loading = true;
      this.error = null;
      
      try {
        const response = await fetch('/api/data', {
          signal: this.controller.signal
        });
        this.data = await response.json();
      } catch (err) {
        if (err.name !== 'AbortError') {
          this.error = err.message;
        }
      } finally {
        this.loading = false;
      }
    }
  }
});// Component with all 7 lifecycle hooks
XTool.registerComponent({
  name: 'full-lifecycle',
  template: `<div><p>Check console for lifecycle logs</p></div>`,
  
  makeData: () => ({ value: 'Hello' }),
  
  // 1. Before component is mounted to DOM
  beforeMount() {
    console.log('1. beforeMount: Component created, not in DOM yet');
  },
  
  // 2. After component is mounted to DOM
  mounted() {
    console.log('2. mounted: Component in DOM, directives active');
  },
  
  // 3. After reactive data changes (can happen multiple times)
  updated() {
    console.log('3. updated: Data changed, DOM updated');
  },
  
  // 4. Before component is removed from DOM
  beforeUnmount() {
    console.log('4. beforeUnmount: About to remove from DOM');
  },
  
  // 5. After component is removed from DOM
  unmounted() {
    console.log('5. unmounted: Removed from DOM');
  },
  
  // 6. Before component is completely destroyed
  beforeDestroy() {
    console.log('6. beforeDestroy: About to destroy component');
  },
  
  // 7. After component is completely destroyed
  destroyed() {
    console.log('7. destroyed: Component fully destroyed');
  }
});Reactive Props
Pass reactive data between components. Props automatically update when parent data changes.
Parent to Child
Data flows down via x-prop
x-prop="{ name: userName, count: userCount }"
            Auto Updates
Child rerenders when props change
this.name // Always current value
            Usage Pattern
User Card ExampleProps are passed as an object and automatically become available in the component.
See Complete Example
// Component receives props automatically
XTool.registerComponent({
  name: 'user-card',
  template: `<div><h3>\{{ name }}</h3><span>\{{ count }}</span></div>`
});
// Parent passes data via x-prop
<component source="user-card" x-prop="{ name: userName, count: userCount }"></component>Prop Effects
React to specific prop changes with custom logic and side effects.
Reactive Side Effects
Message Changes
Show notifications when new messages arrive
User ID Updates
Fetch fresh user data when ID changes
Theme Changes
Update styles and animations dynamically
// Notification component with auto-hide
XTool.registerComponent({
  name: 'notification',
  template: `<div x-show="visible" 
              class="notification">
    <span x-text="message"></span>
    <button x-on:click="hide">✕</button>
  </div>`,
  
  makeData: () => ({
    visible: false,
    timeout: null
  }),
  
  propEffects: {
    message(newMessage, oldMessage) {
      if (newMessage && newMessage !== oldMessage) {
        this.visible = true;
        this.autoHide();
      }
    }
  },
  
  methods: {
    hide() {
      this.visible = false;
    },
    autoHide() {
      clearTimeout(this.timeout);
      this.timeout = setTimeout(() => {
        this.visible = false;
      }, 3000);
    }
  }
});// User profile with data fetching
XTool.registerComponent({
  name: 'user-profile',
  template: `<div>
    <div x-show="loading">Loading...</div>
    <div x-show="!loading && user">
      <h3 x-text="user.name"></h3>
      <p x-text="user.email"></p>
    </div>
  </div>`,
  
  makeData: () => ({
    user: null,
    loading: false
  }),
  
  propEffects: {
    userId(newUserId, oldUserId) {
      if (newUserId && newUserId !== oldUserId) {
        this.fetchUser(newUserId);
      }
    }
  },
  
  methods: {
    async fetchUser(id) {
      this.loading = true;
      try {
        const response = await fetch(`/api/users/${id}`);
        this.user = await response.json();
      } finally {
        this.loading = false;
      }
    }
  }
});Slots & Content Projection
Create flexible, reusable components by allowing custom content insertion.
Default Slot
Main content area
<slot></slot>
            Named Slots
Specific content areas
slot="header"
            Flexible
Any HTML content
Components, text, etc.
            Card Component Pattern
Header + Body + FooterCommon pattern: define slots for header, body, and footer content areas.
See Card Implementation
// Define slots in template
template: `<div><header><slot name="header"></slot></header><main><slot></slot></main></div>`
// Use with custom content
<component source="card">
  <h2 slot="header">Title</h2>
  <p>Body content goes here</p>
</component>Methods & Computed Properties
Add interactive behavior and smart calculated values to your components.
Methods
Event handlers & actions
Computed
Smart cached values
Component Logic
Methods
Handle user interactions, form submissions, and data manipulation
Computed Properties
Smart cached values that update automatically when dependencies change
// Todo List Component
XTool.registerComponent({
  name: 'todo-list',
  template: `<div>
    <input x-model="newTodo" x-on:keyup.enter="addTodo">
    <button x-on:click="addTodo">Add</button>
    <ul>
      <li x-for="todo in todos" x-text="todo"></li>
    </ul>
  </div>`,
  
  makeData: () => ({
    todos: [],
    newTodo: ''
  }),
  
  methods: {
    addTodo() {
      if (this.newTodo.trim()) {
        this.todos.push(this.newTodo);
        this.newTodo = '';
      }
    },
    removeTodo(index) {
      this.todos.splice(index, 1);
    }
  }
});// Shopping Cart Component
XTool.registerComponent({
  name: 'shopping-cart',
  template: `<div>
    <div x-for="item in items">
      \{{ item.name }}: $\{{ item.price }}
    </div>
    <p>Total: $\{{ total }}</p>
    <p>Tax: $\{{ tax }}</p>
    <p>Grand Total: $\{{ grandTotal }}</p>
  </div>`,
  
  makeData: () => ({
    items: [
      { name: 'Apple', price: 1.50 },
      { name: 'Banana', price: 0.75 }
    ],
    taxRate: 0.08
  }),
  
  computed: {
    total() {
      return this.items.reduce((sum, item) => 
        sum + item.price, 0);
    },
    tax() {
      return this.total * this.taxRate;
    },
    grandTotal() {
      return this.total + this.tax;
    }
  }
});