πΎ Local Storage
OnigiriJS makes data persistence simple with an easy-to-use storage API, automatic expiration, and support for both local and session storage.
Basic Operations
Set Data
// Store simple values
Onigiri.storage.set('username', 'chef');
Onigiri.storage.set('theme', 'dark');
// Store objects (automatically serialized)
Onigiri.storage.set('user', {
name: 'Chef',
email: 'chef@onigiri.com',
favoriteOnigiri: 'salmon',
preferences: {
notifications: true,
newsletter: false
}
});
// Store arrays
Onigiri.storage.set('cart', [
{ id: 1, name: 'Salmon Onigiri', price: 3.50 },
{ id: 2, name: 'Tuna Onigiri', price: 3.50 }
]);
Get Data
// Get simple value
const username = Onigiri.storage.get('username');
console.log(username); // 'chef'
// Get object
const user = Onigiri.storage.get('user');
console.log(user.name); // 'Chef'
// Get with default value
const theme = Onigiri.storage.get('theme', 'light');
const lang = Onigiri.storage.get('language', 'en');
Remove Data
// Remove specific item
Onigiri.storage.remove('theme');
// Remove multiple items
['temp1', 'temp2', 'cache'].forEach(key => {
Onigiri.storage.remove(key);
});
Clear All Data
// Clear all storage (respects prefix)
Onigiri.storage.clear();
// Caution: This removes all data with your app's prefix
β° Expiration
Set with Expiration
// Expire after 1 hour (3,600,000 milliseconds)
Onigiri.storage.set('session', userData, {
expires: 3600000
});
// Expire after 24 hours
Onigiri.storage.set('dailySpecial', {
onigiri: 'Spicy Tuna',
price: 3.99,
date: new Date()
}, {
expires: 24 * 60 * 60 * 1000
});
// Expire after 30 minutes
Onigiri.storage.set('tempData', data, {
expires: 30 * 60 * 1000
});
// Expire after 7 days
Onigiri.storage.set('userPreferences', prefs, {
expires: 7 * 24 * 60 * 60 * 1000
});
Auto-Expiration
// Data automatically expires
Onigiri.storage.set('code', 'ABC123', { expires: 300000 }); // 5 min
// After 5 minutes...
const code = Onigiri.storage.get('code');
console.log(code); // null (expired and removed)
// Get with default for expired data
const session = Onigiri.storage.get('session', { guest: true });
// Returns default if expired
π Utility Methods
Check if Key Exists
// Check existence
if (Onigiri.storage.has('user')) {
console.log('User data found!');
const user = Onigiri.storage.get('user');
}
// Conditional loading
if (!Onigiri.storage.has('appData')) {
loadDataFromServer();
}
Get All Keys
// Get all storage keys
const keys = Onigiri.storage.keys();
console.log('Stored keys:', keys);
// List all data
keys.forEach(key => {
const value = Onigiri.storage.get(key);
console.log(\`\${key}:\`, value);
});
Get All Items with Prefix
// Get all user preferences
const userPrefs = Onigiri.storage.getAll('user_');
// Returns: { 'user_theme': 'dark', 'user_lang': 'en', ... }
// Get all onigiri data
const onigiriData = Onigiri.storage.getAll('onigiri_');
// Get all temporary data
const tempData = Onigiri.storage.getAll('temp_');
Get Storage Size
// Get number of items
const itemCount = Onigiri.storage.size();
console.log(\`Storage contains \${itemCount} items\`);
π― Storage Prefix
Set Prefix
// Set prefix for your app (default: 'onigiri_')
Onigiri.storage.setPrefix('myapp_');
// Now all keys are prefixed
Onigiri.storage.set('user', data);
// Stored as: myapp_user
// Different apps can use different prefixes
Onigiri.storage.setPrefix('blog_');
Onigiri.storage.set('posts', posts);
// Stored as: blog_posts
Why Use Prefixes?
// Prevents conflicts between apps
// App 1
Onigiri.storage.setPrefix('shop_');
Onigiri.storage.set('cart', shopCart);
// App 2
Onigiri.storage.setPrefix('blog_');
Onigiri.storage.set('cart', blogCart);
// No conflict! Different storage keys:
// shop_cart vs blog_cart
π« Session Storage
Session Storage Basics
// Session storage (cleared on tab/window close)
Onigiri.storage.session.set('tempCart', {
items: ['Salmon Onigiri', 'Tuna Onigiri'],
total: 7.00
});
// Retrieve session data
const cart = Onigiri.storage.session.get('tempCart');
// Get with default
const wizardStep = Onigiri.storage.session.get('wizardStep', 1);
// Remove session data
Onigiri.storage.session.remove('tempCart');
// Clear all session data
Onigiri.storage.session.clear();
Session vs Local Storage
// Local Storage: Persists indefinitely
Onigiri.storage.set('userPreferences', prefs);
// Available across browser sessions
// Session Storage: Cleared on tab close
Onigiri.storage.session.set('tempData', data);
// Lost when tab/window closes
// Use cases:
// Local: User settings, saved data, cache
// Session: Wizard state, temp forms, one-time data
π‘ Practical Examples
User Preferences
const preferences = {
save() {
Onigiri.storage.set('preferences', {
theme: this.theme,
language: this.language,
notifications: this.notifications
});
},
load() {
const saved = Onigiri.storage.get('preferences', {
theme: 'light',
language: 'en',
notifications: true
});
Object.assign(this, saved);
this.apply();
},
apply() {
document.body.className = \`theme-\${this.theme}\`;
document.documentElement.lang = this.language;
},
reset() {
Onigiri.storage.remove('preferences');
this.load(); // Revert to defaults
}
};
preferences.load();
Shopping Cart Persistence
const cart = new Onigiri.prototype.Component({
data: {
items: [],
total: 0
},
computed: {
itemCount() {
return this.items.reduce((sum, item) => sum + item.quantity, 0);
}
},
methods: {
addItem(item) {
const existing = this.items.find(i => i.id === item.id);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...item, quantity: 1 });
}
this.updateTotal();
this.save();
},
removeItem(itemId) {
this.items = this.items.filter(i => i.id !== itemId);
this.updateTotal();
this.save();
},
updateTotal() {
this.total = this.items.reduce((sum, item) => {
return sum + (item.price * item.quantity);
}, 0);
},
save() {
Onigiri.storage.set('cart', {
items: this.items,
total: this.total,
updated: new Date()
});
},
clear() {
this.items = [];
this.total = 0;
Onigiri.storage.remove('cart');
}
},
created() {
// Load saved cart
const saved = Onigiri.storage.get('cart');
if (saved) {
this.items = saved.items;
this.total = saved.total;
console.log('π Cart restored from storage');
}
}
});
Form Draft Auto-Save
const formDraft = {
saveInterval: null,
init() {
const form = O('#article-form');
// Auto-save every 30 seconds
this.saveInterval = setInterval(() => {
this.saveDraft();
}, 30000);
// Save on input (debounced)
const debouncedSave = Onigiri.debounce(() => {
this.saveDraft();
}, 2000);
form.on('input', 'input, textarea', debouncedSave);
// Load draft on init
this.loadDraft();
},
saveDraft() {
const draft = {
title: O('[name="title"]').val(),
content: O('[name="content"]').val(),
tags: O('[name="tags"]').val(),
timestamp: new Date()
};
Onigiri.storage.set('article_draft', draft, {
expires: 7 * 24 * 60 * 60 * 1000 // 7 days
});
O('#save-indicator').text('π Draft saved').fadeIn(200);
setTimeout(() => {
O('#save-indicator').fadeOut(200);
}, 2000);
},
loadDraft() {
const draft = Onigiri.storage.get('article_draft');
if (draft) {
O('[name="title"]').val(draft.title);
O('[name="content"]').val(draft.content);
O('[name="tags"]').val(draft.tags);
const time = new Date(draft.timestamp);
alert(\`π Draft restored from \${time.toLocaleString()}\`);
}
},
clearDraft() {
Onigiri.storage.remove('article_draft');
clearInterval(this.saveInterval);
}
};
formDraft.init();
Recent Searches
const searchHistory = {
maxItems: 10,
add(query) {
if (!query.trim()) return;
let history = Onigiri.storage.get('search_history', []);
// Remove if already exists
history = history.filter(q => q !== query);
// Add to beginning
history.unshift(query);
// Limit to maxItems
history = history.slice(0, this.maxItems);
// Save
Onigiri.storage.set('search_history', history);
},
get() {
return Onigiri.storage.get('search_history', []);
},
clear() {
Onigiri.storage.remove('search_history');
},
render() {
const history = this.get();
const container = O('#search-history');
container.empty();
if (history.length === 0) {
container.html('<p>No recent searches</p>');
return;
}
history.forEach(query => {
container.append(\`
<div class="history-item">
<a href="#" data-query="\${query}">
π \${query}
</a>
</div>
\`);
});
}
};
// Add to history on search
O('#search-form').on('submit', function(e) {
const query = O('#search-input').val();
searchHistory.add(query);
});
// Show history
searchHistory.render();
Theme Switcher
const themeManager = {
themes: ['light', 'dark', 'auto'],
current: 'light',
init() {
// Load saved theme
this.current = Onigiri.storage.get('theme', 'light');
this.apply();
// Theme switcher buttons
O('.theme-btn').on('click', function() {
const theme = O(this).data('theme');
themeManager.setTheme(theme);
});
},
setTheme(theme) {
if (!this.themes.includes(theme)) return;
this.current = theme;
Onigiri.storage.set('theme', theme);
this.apply();
},
apply() {
// Remove all theme classes
this.themes.forEach(t => {
document.body.classList.remove(\`theme-\${t}\`);
});
// Apply current theme
document.body.classList.add(\`theme-\${this.current}\`);
// Update active button
O('.theme-btn').removeClass('active');
O(\`.theme-btn[data-theme="\${this.current}"]\`).addClass('active');
console.log(\`π Theme applied: \${this.current}\`);
}
};
themeManager.init();
Component State Persistence
const todoApp = new Onigiri.prototype.Component({
data: {
todos: [],
filter: 'all',
lastSync: null
},
methods: {
addTodo(text) {
this.todos.push({
id: Date.now(),
text: text,
completed: false,
createdAt: new Date()
});
},
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
},
watchers: {
todos() {
// Auto-save on any change
this.saveState();
},
filter() {
this.saveState();
}
},
created() {
// Load saved state
this.loadState();
},
saveState() {
Onigiri.storage.set('todoApp', {
todos: this.todos,
filter: this.filter,
lastSync: new Date()
});
},
loadState() {
const saved = Onigiri.storage.get('todoApp');
if (saved) {
this.todos = saved.todos;
this.filter = saved.filter;
this.lastSync = saved.lastSync;
console.log('π State restored');
}
}
});
π Best Practice: Always provide default values when getting data to handle missing or expired items gracefully!
Storage Best Practices
- Use meaningful, consistent key names
- Always provide default values with
get() - Set prefixes to avoid naming conflicts
- Use expiration for temporary data
- Don't store sensitive data (passwords, tokens)
- Keep stored data size reasonable (<5MB recommended)
- Use session storage for temporary UI state
- Clear old/unused data periodically
- Handle storage quota errors gracefully
- Serialize complex objects before storing
Storage Limits
// Check available space (approximate)
function checkStorageSpace() {
let total = 0;
for (let key in localStorage) {
if (localStorage.hasOwnProperty(key)) {
total += localStorage[key].length + key.length;
}
}
const used = (total / 1024).toFixed(2);
console.log(\`Storage used: \${used} KB\`);
// Most browsers allow ~5-10MB
}
β Development Roadmap
Track the progress of OnigiriJS modules. Tasks are marked complete by the development team.
OnigiriJS Module Roadmap
Implementation progress of planned modules
6 / 21 completed (29%)
onigiri-state
Shared global & scoped state management
onigiri-directives
Declarative DOM bindings (o-show, o-model, etc.)
onigiri-resource
REST-style data models over AJAX
onigiri-observe
Intersection & Mutation observer helpers
onigiri-humhub-ui
Standard HumHub UI abstractions (modal, notify, confirm)
onigiri-lifecycle
Component lifecycle hooks
onigiri-guard
Debounce, throttle, single-run guards
onigiri-scroll
Scroll save/restore & helpers (PJAX-friendly)
onigiri-permission
Client-side permission awareness
onigiri-portal
DOM teleport / overlay mounting
onigiri-router
Micro router (non-SPA, PJAX-first)
onigiri-sanitize
HTML & input sanitization
onigiri-shortcut
Keyboard shortcut manager
onigiri-queue
Sequential async task runner
onigiri-gesture
Touch & swipe helpers
onigiri-devtools
Debugging & inspection helpers
onigiri-plugin
Plugin registration system
onigiri-time
Relative time & timezone utilities
onigiri-emojis
Emoji Picker and Manager
onigiri-tasks
Task Management
onigiri-polls
Polls creation and management
Note: Task completion is managed by the OnigiriJS development team.