
Security News
Axios Supply Chain Attack Reaches OpenAI macOS Signing Pipeline, Forces Certificate Rotation
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.
@zogjs/component
Advanced tools
A powerful component system plugin for Zog.js that enables you to create reusable, encapsulated UI components with props, events, slots, and scoped reactivity.
npm install zogjs @zogjs/component
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>
:prop-name syntax@event-name handlersregisterComponent('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'
};
}
});
z-if, z-for, z-model, etc.)setup() and propsProps allow you to pass data from parent to child components.
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>
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-name): Update automatically when parent data changesprop-name): String values that don't changeComponents can emit events to communicate with their parent.
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
};
}
});
<z-custom-button
label="Save"
@button-clicked="onSave">
</z-custom-button>
const app = createApp(() => ({
onSave(event) {
console.log('Button clicked at:', event.timestamp);
}
}));
Slots allow you to pass content from parent to child.
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.
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>
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>
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', { ... });
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 templatesetup (function, optional): Setup function that receives props and contextReturns: void
setup(props, context)
Parameters:
props (object): All props passed to the componentcontext (object):
emit(eventName, ...args): Function to emit events to parentReturns: Object with data and methods for the template
:prop-name syntax for reactive propsref() or reactive())emit() and @event-nameMIT
Made with ❤️ for nothing
FAQs
ZogJS component plugin
We found that @zogjs/component demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

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.

Security News
OpenAI rotated macOS signing certificates after a malicious Axios package reached its CI pipeline in a broader software supply chain attack.

Security News
Open source is under attack because of how much value it creates. It has been the foundation of every major software innovation for the last three decades. This is not the time to walk away from it.

Security News
Socket CEO Feross Aboukhadijeh breaks down how North Korea hijacked Axios and what it means for the future of software supply chain security.