Components

ichigo.js components are real Custom Elements backed by the same reactivity system. Define one with defineComponent, pointing it at a <template> for its markup. Components use Light DOM, not Shadow DOM.

defineComponent

<!-- Component markup -->
<template id="my-list">
  <ul v-if="items.length > 0">
    <li v-for="item of items" :key="item.id">{{ item.name }}</li>
  </ul>
  <!-- Fallback projected from the parent -->
  <slot></slot>
</template>
import { defineComponent } from '@mintjamsinc/ichigojs';

defineComponent('my-list', {
  template: '#my-list',   // CSS selector for the <template>
  props: ['items'],       // props received from the parent
  data() {
    // props are available via this and can be defaulted/transformed here
    return { items: this.items ?? [] };
  }
});
<!-- Usage -->
<my-list :items="searchResults">
  <span slot="empty">No results.</span>
</my-list>

defineComponent(tagName, options) accepts the same options as createApp, plus two more:

  • template — CSS selector for the <template> that defines the markup (required)
  • props — array of property names received from the parent via attribute/property binding

Props

  • Declared via the props array. Each declared prop becomes a property on the custom element, so the parent can bind to it with v-bind / : (e.g. :items="searchResults").
  • Props are reactive from the start and are included in the component's data automatically. Values returned from data() take precedence, so you can default or transform a prop (e.g. this.items ?? []).
defineComponent('user-badge', {
  template: '#user-badge',
  props: ['name', 'role'],
  computed: {
    label() { return `${this.name} (${this.role})`; }
  }
});

Slots

Place a native <slot> inside the component <template> to project content from the parent. Named slots are supported.

<template id="modal-box">
  <div class="modal">
    <header><slot name="title">Title</slot></header>
    <div class="body"><slot></slot></div>
  </div>
</template>
<modal-box>
  <span slot="title">Confirm</span>
  <p>Are you sure you want to delete this?</p>
</modal-box>

Custom events ($emit)

Components (and applications) can dispatch custom events with $emit, available in both templates and methods. By default the event bubbles from the component's root element, so the parent can listen with @.

defineComponent('my-button', {
  template: '#my-button',
  // Optional: declare the events this component emits. Emitting an
  // undeclared event logs a development warning (validation only; it
  // never blocks dispatch). Omit emits to allow any event name.
  emits: ['selected'],
  methods: {
    onClick() {
      // $emit(name, detail?, options?)
      this.$emit('selected', { id: 42 });
    }
  }
});
<!-- Parent listens; the payload is in event.detail -->
<my-button @selected="onSelected"></my-button>
methods: {
  onSelected(event) {
    console.log(event.detail.id); // 42
  }
}

$emit(name, detail?, options?)

  • name — the event name (listened to as @name on the parent)
  • detail — the payload exposed as event.detail
  • options (VEmitOptions) —
    • bubbles — whether the event bubbles (default: true)
    • cancelable — whether preventDefault() has an effect (default: true); $emit returns false when a listener calls preventDefault()
    • composed — whether the event crosses shadow DOM boundaries (default: false)
    • target — the dispatch target (default: the application root element). Set to document / window for a global event bus.

Refs on components

A ref placed on a component resolves to its host custom element, so you can call DOM methods like focus() or getBoundingClientRect(). See template refs in the Core guide.

<my-card ref="card"></my-card>
this.$refs.card; // the <my-card> element

Apps and components

Apps created with VDOM.createApp and components created with defineComponent share the same reactivity foundation. Options such as data / computed / methods / watch / emits / logLevel, and helpers like $markRaw / $nextTick / $emit / $refs, are common to both. The difference is that a component has template and props and is registered as a custom element.

Deprecated: v-component

⚠️ The v-component directive and VComponentRegistry are deprecated and will be removed in a future release. Use defineComponent (Custom Elements) for new code.

Next steps