New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details
Socket
Book a DemoSign in
Socket

@zogjs/component

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zogjs/component

ZogJS component plugin

latest
Source
npmnpm
Version
0.3.0
Version published
Maintainers
1
Created
Source

@zogjs/component

A powerful component system plugin for Zog.js that enables you to create reusable, encapsulated UI components with props, events, slots, and scoped reactivity.

Installation

npm install zogjs @zogjs/component

Quick Start

import { createApp } from 'zogjs';
import { ComponentPlugin } from '@zogjs/component';

const app = createApp(() => ({
  message: 'Hello from parent'
}));

// Install the plugin and get access to registerComponent
const { registerComponent } = app.use(ComponentPlugin);

// Register a simple component
registerComponent('greeting', {
  template: `<div class="greeting">{{ message }}</div>`,
  setup(props) {
    return {
      message: props.message || 'Hello!'
    };
  }
});

app.mount('#app');
<div id="app">
  <z-greeting :message="message"></z-greeting>
</div>

Features

  • Reactive Props: Pass data from parent to child with :prop-name syntax
  • Custom Events: Emit events from child to parent with @event-name handlers
  • Slots: Project content from parent into child components
  • Scoped Reactivity: Each component has its own isolated reactive scope
  • Setup Function: Define component logic with access to props and emit
  • Kebab-case Support: Automatically converts kebab-case props to camelCase
  • Class & Style Merging: Parent classes and styles are merged with component root

Component Structure

Basic Component Definition

registerComponent('component-name', {
  template: '<div>{{ content }}</div>',
  setup(props, { emit }) {
    // props: object containing all passed props
    // emit: function to emit events to parent
    
    // Return reactive data for the template
    return {
      content: props.initialValue || 'default'
    };
  }
});

Template Requirements

  • Must have a single root element
  • Can use all Zog.js directives (z-if, z-for, z-model, etc.)
  • Has access to data returned from setup() and props

Props

Props allow you to pass data from parent to child components.

Passing Props

Use the : prefix for reactive props:

<!-- Reactive prop (updates when parent data changes) -->
<z-counter :initial-value="startCount"></z-counter>

<!-- Static prop -->
<z-button label="Click me"></z-button>

<!-- Multiple props -->
<z-card :title="cardTitle" :description="cardDesc" theme="dark"></z-card>

Receiving Props

Props are automatically converted from kebab-case to camelCase:

registerComponent('counter', {
  template: `<div>Count: {{ count }}</div>`,
  setup(props) {
    // :initial-value becomes props.initialValue
    const count = ref(props.initialValue || 0);
    return { count };
  }
});

Prop Types

  • Reactive props (:prop-name): Update automatically when parent data changes
  • Static props (prop-name): String values that don't change

Events

Components can emit events to communicate with their parent.

Emitting Events

registerComponent('custom-button', {
  template: `
    <button @click="handleClick">
      {{ label }}
    </button>
  `,
  setup(props, { emit }) {
    const handleClick = () => {
      emit('button-clicked', { timestamp: Date.now() });
    };
    
    return {
      label: props.label || 'Click',
      handleClick
    };
  }
});

Listening to Events

<z-custom-button 
  label="Save" 
  @button-clicked="onSave">
</z-custom-button>
const app = createApp(() => ({
  onSave(event) {
    console.log('Button clicked at:', event.timestamp);
  }
}));

Slots

Slots allow you to pass content from parent to child.

Using Slots

In your component template, use <slot></slot>:

registerComponent('card', {
  template: `
    <div class="card">
      <h2>{{ title }}</h2>
      <div class="card-body">
        <slot></slot>
      </div>
    </div>
  `,
  setup(props) {
    return { title: props.title };
  }
});

Pass content to the slot:

<z-card title="User Profile">
  <p>Name: John Doe</p>
  <p>Email: john@example.com</p>
</z-card>

Note: Currently, only default (unnamed) slots are supported.

Simple Example

A basic counter component:

import { createApp, ref } from 'zogjs';
import { ComponentPlugin } from '@zogjs/component';

const app = createApp(() => ({
  initialCount: 0
}));

const { registerComponent } = app.use(ComponentPlugin);

registerComponent('simple-counter', {
  template: `
    <div class="counter">
      <button @click="decrement">-</button>
      <span class="count">{{ count }}</span>
      <button @click="increment">+</button>
    </div>
  `,
  setup(props, { emit }) {
    const count = ref(props.initialValue || 0);
    
    const increment = () => {
      count.value++;
      emit('change', count.value);
    };
    
    const decrement = () => {
      count.value--;
      emit('change', count.value);
    };
    
    return { count, increment, decrement };
  }
});

app.mount('#app');
<div id="app">
  <simple-counter 
    :initial-value="initialCount" 
    @change="initialCount = $event">
  </simple-counter>
  
  <p>Current value: {{ initialCount }}</p>
</div>

Advanced Example

A more complex todo list component with multiple features:

import { createApp, ref, reactive, computed } from 'zogjs';
import { ComponentPlugin } from '@zogjs/component';

const app = createApp(() => ({
  todos: [
    { id: 1, text: 'Learn Zog.js', done: false },
    { id: 2, text: 'Build a component', done: true }
  ],
  handleTodoChange(todos) {
    console.log('Todos updated:', todos);
  }
}));

const { registerComponent } = app.use(ComponentPlugin);

// Todo Item Component
registerComponent('todo-item', {
  template: `
    <li :class="{ completed: todo.done, editing: isEditing }">
      <div class="view" z-show="!isEditing">
        <input 
          type="checkbox" 
          :checked="todo.done" 
          @change="toggleDone">
        <label @dblclick="startEdit">{{ todo.text }}</label>
        <button @click="remove" class="destroy">×</button>
      </div>
      <input 
        class="edit" 
        z-show="isEditing"
        :value="editText"
        @input="editText = $event.target.value"
        @blur="finishEdit"
        @keyup.enter="finishEdit"
        @keyup.escape="cancelEdit">
    </li>
  `,
  setup(props, { emit }) {
    const isEditing = ref(false);
    const editText = ref('');
    
    const toggleDone = () => {
      emit('toggle', props.todo.id);
    };
    
    const remove = () => {
      emit('remove', props.todo.id);
    };
    
    const startEdit = () => {
      isEditing.value = true;
      editText.value = props.todo.text;
    };
    
    const finishEdit = () => {
      if (editText.value.trim()) {
        emit('edit', { id: props.todo.id, text: editText.value.trim() });
      }
      isEditing.value = false;
    };
    
    const cancelEdit = () => {
      isEditing.value = false;
      editText.value = props.todo.text;
    };
    
    return {
      isEditing,
      editText,
      toggleDone,
      remove,
      startEdit,
      finishEdit,
      cancelEdit
    };
  }
});

// Todo List Component
registerComponent('todo-list', {
  template: `
    <div class="todo-app">
      <header>
        <h1>{{ title }}</h1>
        <input 
          class="new-todo"
          placeholder="What needs to be done?"
          :value="newTodoText"
          @input="newTodoText = $event.target.value"
          @keyup.enter="addTodo">
      </header>
      
      <section class="main" z-show="todos.length > 0">
        <input 
          id="toggle-all" 
          class="toggle-all" 
          type="checkbox"
          :checked="allDone"
          @change="toggleAll">
        <label for="toggle-all">Mark all as complete</label>
        
        <ul class="todo-list">
          <z-todo-item
            z-for="todo in filteredTodos"
            :key="todo.id"
            :todo="todo"
            @toggle="toggleTodo"
            @remove="removeTodo"
            @edit="editTodo">
          </z-todo-item>
        </ul>
      </section>
      
      <footer class="footer" z-show="todos.length > 0">
        <span class="todo-count">
          {{ remaining }} {{ remaining === 1 ? 'item' : 'items' }} left
        </span>
        <ul class="filters">
          <li><a :class="{ selected: filter === 'all' }" @click="filter = 'all'">All</a></li>
          <li><a :class="{ selected: filter === 'active' }" @click="filter = 'active'">Active</a></li>
          <li><a :class="{ selected: filter === 'completed' }" @click="filter = 'completed'">Completed</a></li>
        </ul>
        <button 
          class="clear-completed" 
          @click="clearCompleted"
          z-show="completedCount > 0">
          Clear completed
        </button>
      </footer>
    </div>
  `,
  setup(props, { emit }) {
    const todos = ref(props.initialTodos || []);
    const newTodoText = ref('');
    const filter = ref('all');
    
    const remaining = computed(() => 
      todos.value.filter(t => !t.done).length
    );
    
    const completedCount = computed(() => 
      todos.value.filter(t => t.done).length
    );
    
    const allDone = computed(() => 
      todos.value.length > 0 && remaining.value === 0
    );
    
    const filteredTodos = computed(() => {
      if (filter.value === 'active') {
        return todos.value.filter(t => !t.done);
      }
      if (filter.value === 'completed') {
        return todos.value.filter(t => t.done);
      }
      return todos.value;
    });
    
    const addTodo = () => {
      const text = newTodoText.value.trim();
      if (text) {
        todos.value.push({
          id: Date.now(),
          text,
          done: false
        });
        newTodoText.value = '';
        emit('change', todos.value);
      }
    };
    
    const toggleTodo = (id) => {
      const todo = todos.value.find(t => t.id === id);
      if (todo) {
        todo.done = !todo.done;
        emit('change', todos.value);
      }
    };
    
    const removeTodo = (id) => {
      const index = todos.value.findIndex(t => t.id === id);
      if (index > -1) {
        todos.value.splice(index, 1);
        emit('change', todos.value);
      }
    };
    
    const editTodo = ({ id, text }) => {
      const todo = todos.value.find(t => t.id === id);
      if (todo) {
        todo.text = text;
        emit('change', todos.value);
      }
    };
    
    const toggleAll = () => {
      const done = !allDone.value;
      todos.value.forEach(todo => todo.done = done);
      emit('change', todos.value);
    };
    
    const clearCompleted = () => {
      todos.value = todos.value.filter(t => !t.done);
      emit('change', todos.value);
    };
    
    return {
      todos,
      newTodoText,
      filter,
      remaining,
      completedCount,
      allDone,
      filteredTodos,
      addTodo,
      toggleTodo,
      removeTodo,
      editTodo,
      toggleAll,
      clearCompleted,
      title: props.title || 'todos'
    };
  }
});

app.mount('#app');
<div id="app">
  <todo-list 
    :initial-todos="todos"
    title="My Tasks"
    @change="handleTodoChange">
  </todo-list>
</div>

Best Practices

  • Single Root Element: Always ensure your template has exactly one root element.

  • Prop Naming: Use kebab-case in HTML and camelCase in JavaScript:

    <z-component :initial-value="data"></z-component>
    
    setup(props) {
      console.log(props.initialValue); // camelCase
    }
    
  • Event Naming: Use descriptive event names and pass relevant data:

    emit('item-selected', { id: item.id, name: item.name });
    
  • Reactivity: Use ref() or reactive() for data that needs to be reactive:

    setup(props) {
      const count = ref(0); // reactive
      const user = reactive({ name: 'John' }); // reactive object
      return { count, user };
    }
    
  • Component Names: Use descriptive, hyphenated names:

    registerComponent('user-profile', { ... });
    registerComponent('data-table', { ... });
    

API Reference

registerComponent(name, definition)

Registers a new component.

Parameters:

  • name (string): Component name (used as <z-name> in templates)
  • definition (object):
    • template (string, required): Component HTML template
    • setup (function, optional): Setup function that receives props and context

Returns: void

Setup Function

setup(props, context)

Parameters:

  • props (object): All props passed to the component
  • context (object):
    • emit(eventName, ...args): Function to emit events to parent

Returns: Object with data and methods for the template

Troubleshooting

Component Not Rendering

  • Ensure the component is registered before mounting the app
  • Check that the template has a single root element
  • Verify the component name matches (case-insensitive)

Props Not Updating

  • Use :prop-name syntax for reactive props
  • Check that the parent data is reactive (using ref() or reactive())

Events Not Firing

  • Verify event handler exists in parent scope
  • Check console for errors in event handler execution
  • Ensure correct event name in both emit() and @event-name

Learn More

  • Zog.js Documentation
  • GitHub Repository
  • Examples

License

MIT

Made with ❤️ for nothing

Keywords

zogjs

FAQs

Package last updated on 18 Dec 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts