Components

Components are classes that extend BaseComponent and register as Custom Elements. They render once, hold real state, and update the DOM surgically.

Defining a component

import { Component, BaseComponent, State } from 'v-ibe';

@Component()
class Counter extends BaseComponent {
  @State count = 0;

  view() {
    return (
      <div>
        <p>Count: {this.count}</p>
        <button onClick={() => this.count++}>
          Increment
        </button>
      </div>
    );
  }
}

@Component() converts the class name to a Custom Element tag (Counteruse-counter) and registers it with the browser.

Using a component

Use it as a JSX tag inside any other component’s view():

<Counter />

Or as a native Custom Element in HTML:

<use-counter></use-counter>

Configuration

@Component({
  styles: [CounterStyles],     // local stylesheet classes
  useShadowDOM: true,          // default: true
  services: [CounterService],  // DI providers for this subtree
})

view()

The view() method returns JSX. It runs once — reactivity handles updates:

view() {
  return (
    <div>
      <h1>{this.title}</h1>
      <p class={this.isActive && 'active'}>Content</p>
      <For each={this.items}>
        {(item) => <TodoItem todo={item} />}
      </For>
    </div>
  );
}

@State creates a signal — when its value changes, only the DOM nodes that reference it update. <For> renders lists with the same granularity — only changed items re-render.

Props

@Prop declares inputs that parent components can pass:

@Component()
class UserCard extends BaseComponent {
  @Prop name: string = '';
  @Prop avatar: string = '';
  @Prop onSelect?: () => void;

  view() {
    return (
      <div onClick={this.onSelect}>
        <img src={this.avatar} />
        <span>{this.name}</span>
      </div>
    );
  }
}

Usage:

<UserCard name={this.user.name} avatar={this.user.avatar} />

Props bound to @State values stay in sync. Event handlers (on*) are passed as callbacks.

Lifecycle

Three optional hooks:

@Component()
class Dashboard extends BaseComponent {
  onInit() {
    // after state/effects initialized, before view
  }

  onConnected() {
    // after view rendered and attached to DOM
  }

  onDisconnected() {
    // cleanup on removal
  }
}

Web Components

Every component is a standard Custom Element. This means native browser APIs work out of the box.

Shadow DOM

Enabled by default. Styles and DOM are fully encapsulated:

@Component({ useShadowDOM: false })
class InlineWidget extends BaseComponent {
  // renders directly into the element, no shadow root
}

Slots

Use <slot> to project children into your component’s template:

@Component()
class Card extends BaseComponent {
  @Prop cardTitle: string = '';

  view() {
    return (
      <div class="card">
        <h2>{this.cardTitle}</h2>
        <slot />
      </div>
    );
  }
}
<Card cardTitle="Profile">
  <p>This content goes into the slot.</p>
  <button>Edit</button>
</Card>

Named slots for multiple insertion points:

@Component()
class PageLayout extends BaseComponent {
  view() {
    return (
      <div class="layout">
        <header><slot name="header" /></header>
        <main><slot /></main>
        <footer><slot name="footer" /></footer>
      </div>
    );
  }
}
<PageLayout>
  <nav slot="header">Navigation</nav>
  <p>Main content</p>
  <span slot="footer">© 2025</span>
</PageLayout>

Composition

Components compose naturally through JSX:

@Component()
class App extends BaseComponent {
  view() {
    return (
      <Layout>
        <Sidebar />
        <main>
          <Router />
        </main>
      </Layout>
    );
  }
}