Skip to content

Components Guide

Defining a Component

ts
import { defineComponent, h } from "nexa-framework";

const MyComponent = defineComponent({
  setup(props, { emit, slots }) {
    const count = signal(0);
    return { count };
  },
  render(ctx) {
    return h("div", null, [String(ctx.count.value)]);
  },
});

Props and Emit

ts
defineComponent({
  setup(props, { emit }) {
    return {
      submit: () => emit("submit", props.value),
    };
  },
  render(ctx) {
    return h("button", { onClick: ctx.submit }, ["Submit"]);
  },
});

Slots

html
<!-- Child component -->
<template>
  <div class="card">
    <header>
      <slot name="header" />
    </header>
    <main>
      <slot />
    </main>
  </div>
</template>
html
<!-- Parent component -->
<template>
  <Card>
    <template #header>
      <h1>Title</h1>
    </template>
    <p>Default slot content</p>
  </Card>
</template>

Scoped Slots

html
<!-- Child passes data to slot -->
<template>
  <ul>
    <li v-for="item in items">
      <slot :item="item" :index="index" />
    </li>
  </ul>
</template>

Lifecycle Hooks

ts
import { onMounted, onBeforeUnmount, onUpdated } from 'nexa-framework'

setup() {
  onMounted(() => console.log('mounted'))
  onBeforeUnmount(() => console.log('cleanup'))
  onUpdated(() => console.log('re-rendered'))
}
  • v-show — toggles display via signal
  • v-model — two-way binding for inputs
  • v-if — conditional rendering
  • v-for — list rendering

Event Modifiers

Nexa supports event modifiers to handle common event patterns efficiently:

  • .prevent — calls event.preventDefault()
  • .stop — calls event.stopPropagation()
  • .self — only triggers if the event was dispatched from the element itself (not a child)
html
<button @click.prevent="submit">Submit</button>
<div @click.self="closeModal">Click outside to close</div>

Component Resolution

Nexa components should be named using PascalCase in templates to distinguish them from standard HTML elements. The compiler automatically resolves these names from the component's setup context.

html
<template>
  <MyCustomButton>Click Me</MyCustomButton>
</template>

<script setup>
  import MyCustomButton from "./MyCustomButton.nexa";
</script>

Transitions

Nexa provides enter/leave transitions via _enter and _leave props on any VNode:

ts
import { h, mount } from "nexa-framework";

const vnode = h(
  "div",
  {
    _enter: "fade", // apply 'fade-enter-*' classes on mount
    _leave: "fade", // apply 'fade-leave-*' classes on unmount
  },
  ["Hello"],
);

Enter Animation

When a VNode with _enter is mounted:

  1. {name}-enter-from + {name}-enter-active classes are applied immediately
  2. On the next animation frame, -enter-from is removed and -enter-to is added
  3. On transitionend/animationend (or 300ms timeout), all enter classes are removed

Leave Animation

When a VNode with _leave is removed:

  1. {name}-leave-from + {name}-leave-active classes are applied
  2. DOM removal is deferred until the animation completes
  3. On the next frame, -leave-from is removed and -leave-to is added
  4. On transitionend/animationend (or 300ms timeout), the element is removed

CSS Example

css
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

Default Prefix

Pass true instead of a name to use the v- prefix:

ts
h("div", { _enter: true }, ["content"]);
// uses v-enter-from, v-enter-active, etc.

Nexa UI Library

Nexa comes with an optional premium component library called nexa-ui-kit. These components are designed with a modern, high-quality aesthetic (glassmorphism, premium shadows, and smooth transitions).

For the complete and detailed API documentation of all 29 components (including props, events, slots, and interactive examples), please refer to the:

👉 nexa-ui-kit API Reference

The package includes:

  • Forms & Inputs: NInput, NPassword, NAutocomplete, NSelect, NMultiSelect, NInputNumber, NCheckbox, NRadio, NSwitch, NChips, NDatepicker
  • Buttons & Layout: NButton, NCard, NModal, NBottomSheet, NTabs
  • Data Display: NDataTable, NPaginator, NTreeMenu, NAvatar, NTag, NBadge
  • Feedback & Loading: NAlert, NToastContainer (with useToast), NProgressBar, NSkeleton, NTooltip
  • Form Container & Validation: NForm, NFormField

Common Pitfalls (Avoid Missing Renders)

  • Signals are explicit: use .value in conditions and comparisons.
    • Good: :disabled="loading.value", v-if="isOpen.value"
    • Bad: :disabled="loading", v-if="isOpen"
  • Some components accept dynamic :class values; prefer strings/objects/arrays consistently.
  • <Teleport> only renders if the target exists; if to="#some-target" does not match any element, the content will not mount.

Usage Example

html
<script setup>
  import { NButton, NCard, useToast } from "nexa-ui-kit";
  const { success } = useToast();
</script>

<template>
  <NCard title="Nexa UI">
    <p>Premium components for your next project.</p>
    <NButton variant="primary" @click="success('Hello!')">Click Me</NButton>
  </NCard>
</template>

Released under the MIT License.