
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.
Essential utilities plugin for Zog.js - HTTP client, storage helpers, DOM directives, event bus, and performance utilities
Essential utilities plugin for Zog.js
ZogKit is a comprehensive utilities plugin that extends Zog.js with powerful features including HTTP client, storage helpers, DOM directives, event bus, and performance utilities.
npm install @zogjs/kit
import { createApp, ref } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
const message = ref('Hello ZogKit!');
return { message };
}).use(ZogKit, {
baseURL: 'https://api.example.com',
timeout: 30000,
storagePrefix: 'myapp_'
});
kit.mount('#app');
When you call .use(ZogKit), it returns an object containing all plugin APIs:
const kit = app.use(ZogKit);
// kit now contains: { $http, $storage, $session, $clipboard, $bus, utils }
Two common patterns:
Pattern 1: Chain everything
const kit = createApp(() => {
async function loadData() {
const { data } = await kit.$http.get('/api/data');
return data;
}
return { loadData };
}).use(ZogKit, { baseURL: 'https://api.example.com' });
kit.mount('#app');
Pattern 2: Separate declarations
const app = createApp(() => {
async function loadData() {
const { data } = await kit.$http.get('/api/data');
return data;
}
return { loadData };
});
const kit = app.use(ZogKit, { baseURL: 'https://api.example.com' });
app.mount('#app');
In templates (auto-unwrapped):
<p>{{ count }}</p> <!-- ✅ No .value needed -->
<button :disabled="isLoading">Submit</button> <!-- ✅ Auto-unwrapped -->
In JavaScript (use .value):
count.value++; // ✅ Must use .value
if (isLoading.value) { } // ✅ Must use .value
In z-for loops (items are ALWAYS refs):
<div z-for="user in users">
<p>{{ user.name }}</p> <!-- ✅ Auto-unwrapped in template -->
<button @click="selectUser(user)">Select</button>
</div>
function selectUser(user) {
// user is a ref from z-for, must use .value
console.log(user.value.name); // ✅
}
kit.$http)A powerful HTTP client with automatic JSON handling, timeout support, and request abortion.
kit.$storage, kit.$session)Enhanced localStorage and sessionStorage with TTL support and reactive state.
z-pre - Skip compilationz-once - Render once without reactivityz-cloak - Hide until compiledz-autofocus - Auto focus elementsz-click-outside - Detect outside clicksz-lazy - Lazy load imagesz-copy - Copy to clipboardkit.$bus)Global event system for component communication.
kit.utils)debounce - Debounce function callsthrottle - Throttle function callskit.$clipboard)Easy-to-use clipboard operations with fallback support.
const kit = app.use(ZogKit, {
// HTTP Client Options
baseURL: '', // Base URL for HTTP requests
timeout: 30000, // Request timeout in milliseconds
headers: {}, // Default headers for all requests
// Storage Options
storagePrefix: 'zog_', // Prefix for storage keys
storageTTL: null // Default TTL for storage items
});
The kit.$http service provides a clean API for making HTTP requests.
import { createApp, ref, watchEffect } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
const users = ref([]);
const loading = ref(false);
const error = ref(null);
async function loadUsers() {
loading.value = true;
error.value = null;
try {
// GET request
const { data } = await kit.$http.get('/users');
users.value = data;
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
}
async function createUser(userData) {
// POST request
const { data } = await kit.$http.post('/users', userData);
users.value.push(data);
}
async function updateUser(id, userData) {
// PUT request
await kit.$http.put(`/users/${id}`, userData);
}
async function deleteUser(id) {
// DELETE request
await kit.$http.delete(`/users/${id}`);
}
// Load users on mount
watchEffect(() => {
loadUsers();
});
return { users, loading, error, createUser, updateUser, deleteUser };
}).use(ZogKit, { baseURL: 'https://api.example.com' });
kit.mount('#app');
// Custom headers
const { data } = await kit.$http.get('/api/data', {
headers: {
'Authorization': 'Bearer token123'
}
});
// Custom timeout
const response = await kit.$http.post('/api/upload', formData, {
timeout: 60000
});
// Full response access
const { data, response, status } = await kit.$http.get('/users');
console.log(status); // 200
console.log(response.headers);
const kit = createApp(() => {
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
await kit.$http.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
return { uploadFile };
}).use(ZogKit);
Enhanced storage with TTL support and reactive state.
kit.$storage)import { createApp } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
// Set value
kit.$storage.set('user', { name: 'John', age: 30 });
// Get value
const user = kit.$storage.get('user');
// With default value
const theme = kit.$storage.get('theme', 'light');
// Set with TTL (5 minutes)
kit.$storage.set('token', 'abc123', 5 * 60 * 1000);
// Check if exists
if (kit.$storage.has('token')) {
console.log('Token exists');
}
// Remove item
function logout() {
kit.$storage.remove('token');
}
// Clear all prefixed items
function clearAll() {
kit.$storage.clear();
}
return { logout, clearAll };
}).use(ZogKit, { storagePrefix: 'myapp_' });
kit.$session)// Same API as $storage
kit.$session.set('tempData', { foo: 'bar' });
const data = kit.$session.get('tempData');
Create reactive state that automatically syncs with storage:
import { createApp } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
// Reactive localStorage
const theme = kit.$storage.reactive('theme', 'light');
function toggleTheme() {
// Changes automatically saved to localStorage
theme.value = theme.value === 'light' ? 'dark' : 'light';
}
return { theme, toggleTheme };
}).use(ZogKit);
kit.mount('#app');
<div id="app">
<p>Current theme: {{ theme }}</p>
<button @click="toggleTheme">Toggle Theme</button>
</div>
Skip Zog compilation entirely for static content:
<div z-pre>
{{ This will not be compiled }}
<span :class="notReactive">Static content</span>
</div>
Render once without reactivity (improves performance for static data):
<div z-once>
<h1>{{ user.name }}</h1>
<p>{{ user.bio }}</p>
</div>
Use case: Display data that won't change after initial render.
Hide element until compilation is complete (prevents flash of uncompiled content):
<div z-cloak>
{{ message }}
</div>
Automatically adds CSS rule: [z-cloak] { display: none !important; }
Automatically focus an element when it appears:
<input z-autofocus type="text" placeholder="Auto-focused">
<!-- Works with z-if -->
<input z-if="showModal" z-autofocus type="text">
Detect clicks outside an element (perfect for dropdowns, modals):
import { createApp, ref } from './zog.js';
const kit = createApp(() => {
const isOpen = ref(false);
function close() {
isOpen.value = false;
}
return { isOpen, close };
}).use(ZogKit);
<div z-click-outside="close" class="dropdown">
<button @click="isOpen = !isOpen">Toggle</button>
<ul z-show="isOpen">
<li>Option 1</li>
<li>Option 2</li>
</ul>
</div>
Lazy load images when they enter the viewport:
<!-- Image loads when scrolled into view -->
<img z-lazy="https://example.com/large-image.jpg" alt="Lazy loaded">
Benefits:
Copy text to clipboard on click:
import { createApp, ref } from './zog.js';
const kit = createApp(() => {
const code = ref('npm install @zogjs/kit');
function showNotification() {
console.log('Copied!');
}
return { code, showNotification };
}).use(ZogKit);
<button z-copy="code">Copy Code</button>
<!-- Listen to copied event -->
<button z-copy="code" @copied="showNotification">Copy</button>
ZogKit adds custom modifiers (.debounce, .throttle) to Zog.js:
<!-- ✅ These work (added by ZogKit): -->
<input @input.debounce.500="search">
<div @scroll.throttle.1000="handleScroll">
<!-- ❌ Standard modifiers DON'T work (Zog.js limitation): -->
<form @submit.prevent="onSubmit"> <!-- .prevent not supported -->
<input @keyup.enter="search"> <!-- .enter not supported -->
<button @click.stop="onClick"> <!-- .stop not supported -->
Workaround for standard modifiers:
const kit = createApp(() => {
function onSubmit(e) {
e.preventDefault(); // ✅ Do it manually
e.stopPropagation(); // ✅ Do it manually
// Your logic here
}
function onKeyup(e) {
if (e.key === 'Enter') { // ✅ Check manually
search();
}
}
return { onSubmit, onKeyup };
}).use(ZogKit);
Delay function execution until after wait time has elapsed:
<!-- Wait 500ms after user stops typing -->
<input @input.debounce.500="search" type="text">
<!-- Default delay is 300ms -->
<input @keyup.debounce="handleInput">
Important: The handler must exist in scope:
const kit = createApp(() => {
const query = ref('');
function search() {
console.log('Searching for:', query.value);
}
return { query, search };
}).use(ZogKit);
Use case: Search inputs, form validation
Execute function at most once per specified time period:
<!-- Execute at most once per 1000ms -->
<div @scroll.throttle.1000="handleScroll">Scrollable content</div>
<!-- Default limit is 300ms -->
<button @click.throttle="saveData">Save</button>
Use case: Scroll handlers, resize handlers, rapid button clicks
Global event system for component communication.
const kit = createApp(() => {
function login(userId) {
// Emit event
kit.$bus.emit('user-login', { userId });
}
// Listen for event
kit.$bus.on('user-login', (data) => {
console.log('User logged in:', data.userId);
});
return { login };
}).use(ZogKit);
import { createApp, watchEffect } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
// Listen to event
const unsubscribe = kit.$bus.on('message', (data) => {
console.log('Received:', data);
});
// Remove listener
function cleanup() {
unsubscribe();
// or
kit.$bus.off('message', handler);
}
// Listen once
kit.$bus.once('init', () => {
console.log('Initialized once');
});
// Emit event
function sendMessage(text) {
kit.$bus.emit('message', { text });
}
// Clear specific event listeners
function clearMessages() {
kit.$bus.clear('message');
}
// Clear all listeners
function clearAll() {
kit.$bus.clear();
}
return { sendMessage, cleanup, clearMessages, clearAll };
}).use(ZogKit);
import { createApp, ref, reactive } from './zog.js';
import ZogKit from './zog-kit.js';
// Notification Manager
const notificationApp = createApp(() => {
const notifications = reactive([]);
notificationKit.$bus.on('notify', (message) => {
notifications.push(message);
setTimeout(() => notifications.shift(), 3000);
});
return { notifications };
}).use(ZogKit);
const notificationKit = notificationApp;
// User Manager that emits notifications
const userApp = createApp(() => {
async function saveUser(userData) {
try {
await userKit.$http.post('/users', userData);
userKit.$bus.emit('notify', {
type: 'success',
text: 'User saved successfully!'
});
} catch (error) {
userKit.$bus.emit('notify', {
type: 'error',
text: 'Failed to save user'
});
}
}
return { saveUser };
}).use(ZogKit);
const userKit = userApp;
Copy text to clipboard with automatic fallback.
const kit = createApp(() => {
const message = ref('');
async function copyToClipboard(text) {
const success = await kit.$clipboard.copy(text);
if (success) {
message.value = 'Copied!';
} else {
message.value = 'Copy failed';
}
}
return { message, copyToClipboard };
}).use(ZogKit);
Features:
import { createApp, ref } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
const query = ref('');
// Create debounced function
const search = kit.utils.debounce(function() {
// API call
kit.$http.get('/search?q=' + query.value);
}, 500);
return { query, search };
}).use(ZogKit);
import { createApp, ref, watchEffect } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
const scrollPosition = ref(0);
const handleScroll = kit.utils.throttle(function() {
scrollPosition.value = window.scrollY;
}, 100);
// Add scroll listener
watchEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
});
return { scrollPosition };
}).use(ZogKit);
Here's a comprehensive example using multiple ZogKit features:
import { createApp, ref, reactive, watchEffect } from './zog.js';
import ZogKit from './zog-kit.js';
const kit = createApp(() => {
const users = ref([]);
const loading = ref(false);
const searchQuery = ref('');
const isDropdownOpen = ref(false);
const selectedUser = ref(null);
// Load users on mount
watchEffect(() => {
loadUsers();
});
// Listen for global events
watchEffect(() => {
const cleanup = kit.$bus.on('user-updated', loadUsers);
return cleanup; // Cleanup on unmount
});
// Load saved preferences
const lastSearch = kit.$storage.get('lastSearch', '');
searchQuery.value = lastSearch;
async function loadUsers() {
loading.value = true;
try {
const { data } = await kit.$http.get('/api/users');
users.value = data;
} catch (error) {
kit.$bus.emit('notify', {
type: 'error',
text: 'Failed to load users'
});
} finally {
loading.value = false;
}
}
// Debounced search
const search = kit.utils.debounce(async function(query) {
kit.$storage.set('lastSearch', query);
try {
const { data } = await kit.$http.get(`/api/search?q=${query}`);
users.value = data;
} catch (error) {
console.error('Search failed:', error);
}
}, 500);
function closeDropdown() {
isDropdownOpen.value = false;
}
async function copyUserId(user) {
// user is a ref from z-for, so use .value
const id = user.value.id;
const success = await kit.$clipboard.copy(id);
if (success) {
kit.$bus.emit('notify', {
type: 'success',
text: 'User ID copied!'
});
}
}
return {
users,
loading,
searchQuery,
isDropdownOpen,
selectedUser,
loadUsers,
search,
closeDropdown,
copyUserId
};
}).use(ZogKit, {
baseURL: 'https://api.example.com',
timeout: 30000,
storagePrefix: 'myapp_'
});
kit.mount('#app');
<div id="app" z-cloak>
<!-- Search Input with Debounce -->
<input
z-autofocus
z-model="searchQuery"
@input="search(searchQuery)"
type="text"
placeholder="Search users..."
>
<!-- Loading State -->
<div z-if="loading">Loading...</div>
<!-- User List -->
<div z-else>
<div z-for="user in users" :key="user.id">
<!-- Lazy Loaded Avatar -->
<img z-lazy="user.avatar" :alt="user.name">
<!-- User Info (Rendered Once) -->
<div z-once>
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
</div>
<!-- Copy User ID - user is a ref from z-for -->
<button @click="copyUserId(user)">Copy ID</button>
</div>
</div>
<!-- Dropdown with Click Outside -->
<div z-click-outside="closeDropdown" class="dropdown">
<button @click="isDropdownOpen = !isDropdownOpen">Options</button>
<ul z-show="isDropdownOpen">
<li>Option 1</li>
<li>Option 2</li>
</ul>
</div>
</div>
<!-- ✅ Good: Static user profile -->
<div z-once>
<h1>{{ user.name }}</h1>
<p>{{ user.bio }}</p>
</div>
<!-- ❌ Bad: Dynamic counter -->
<div z-once>
<p>Count: {{ count }}</p> <!-- Won't update! -->
</div>
<div z-cloak>
<div z-if="loading">Loading...</div>
<div z-else>{{ content }}</div>
</div>
<!-- ❌ Bad: Inline expression with ref -->
<button @click="count.value++">Increment</button>
<!-- ✅ Good: Method handler -->
<button @click="increment">Increment</button>
function increment() {
count.value++;
}
// z-for ALWAYS wraps items in ref()
function selectUser(user) {
// user is a ref from z-for
console.log(user.value.name); // ✅ Use .value
selectedUser.value = user.value;
}
<!-- Search: 300-500ms -->
<input @input.debounce.500="search">
<!-- Scroll: 100-200ms -->
<div @scroll.throttle.100="onScroll">
import { createApp, watchEffect } from './zog.js';
const kit = createApp(() => {
// Setup listener with cleanup
watchEffect(() => {
const unsubscribe = kit.$bus.on('event', handler);
return unsubscribe; // Called on unmount
});
return { };
}).use(ZogKit);
// Token expires in 1 hour
kit.$storage.set('authToken', token, 60 * 60 * 1000);
baseURL is configured correctlykit.$http, not this.$http<!-- ❌ Wrong -->
<p>{{ count.value }}</p>
<!-- ✅ Correct -->
<p>{{ count }}</p>
// ❌ Wrong - function not returned
const kit = createApp(() => {
function myHandler() { }
return { }; // myHandler not exposed!
}).use(ZogKit);
// ✅ Correct - function returned in scope
const kit = createApp(() => {
function myHandler() { }
return { myHandler }; // Now accessible in template
}).use(ZogKit);
| Method | Parameters | Returns | Description |
|---|---|---|---|
get(url, options) | url: string, options: object | Promise | GET request |
post(url, body, options) | url: string, body: any, options: object | Promise | POST request |
put(url, body, options) | url: string, body: any, options: object | Promise | PUT request |
delete(url, options) | url: string, options: object | Promise | DELETE request |
| Method | Parameters | Returns | Description |
|---|---|---|---|
set(key, value, ttl) | key: string, value: any, ttl: number | boolean | Store value |
get(key, defaultValue) | key: string, defaultValue: any | any | Retrieve value |
remove(key) | key: string | void | Remove item |
clear() | - | void | Clear all prefixed items |
has(key) | key: string | boolean | Check if exists |
reactive(key, defaultValue, ttl) | key: string, defaultValue: any, ttl: number | Ref | Reactive storage |
| Method | Parameters | Returns | Description |
|---|---|---|---|
copy(text) | text: string | Promise<boolean> | Copy to clipboard |
| Method | Parameters | Returns | Description |
|---|---|---|---|
on(event, handler) | event: string, handler: function | function | Listen to event (returns unsubscribe) |
off(event, handler) | event: string, handler: function | void | Remove listener |
emit(event, data) | event: string, data: any | void | Emit event |
once(event, handler) | event: string, handler: function | void | Listen once |
clear(event) | event: string | void | Clear listeners for event |
clear() | - | void | Clear all listeners |
| Function | Parameters | Returns | Description |
|---|---|---|---|
debounce(fn, delay) | fn: function, delay: number | function | Debounced function |
throttle(fn, limit) | fn: function, limit: number | function | Throttled function |
Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details.
FAQs
Essential utilities plugin for Zog.js - HTTP client, storage helpers, DOM directives, event bus, and performance utilities
We found that @zogjs/kit 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.