In the modern web ecosystem, starting any web project without reaching for React, Next.js, Vue, or Svelte has almost become taboo. Yet, when our team at Setvy sat down to architect Aksara-Mart—a digital marketplace serving thousands of students and teachers across our vocational high school—we made an intentional, contrarian decision:
Key Decision: We threw out all third-party frontend frameworks and built the entire user interface in pure Vanilla JavaScript, modern modular CSS, and Progressive Web App (PWA) standards.
Why? Because when your primary user base consists of students using second-hand, $80 entry-level Android phones connected to crowded school Wi-Fi, developer convenience cannot come at the expense of end-user performance.
1. The Reality of School Hardware and Campus Networks
Before writing a single line of code, we profiled our target environment in real classroom conditions:
• Hardware Constraints: Approximately 65% of students carry entry-level devices with 2GB to 3GB of RAM, powered by aging Quad-core MediaTek chips or Snapdragon 400-series processors.
• Network Bottlenecks: During recess spikes, hundreds of students connect to the campus Wi-Fi simultaneously, dropping effective bandwidth to congested 2G/3G speeds.
• The 15-Minute Recess Rush: Students have exactly 15 minutes between bells to browse the canteen menu, order food, and pick it up. A 4-second loading screen means they literally don't get lunch.
A barebones Next.js or React e-commerce template typically ships 250 KB to 500 KB of minified JavaScript. On a modern M3 MacBook or iPhone 16, parsing 300 KB of JS takes 15 milliseconds. On a budget Android phone, it takes 1,200 to 2,500 milliseconds. This latency manifests as Hydration Lag (Total Blocking Time): the page looks rendered, but tapping the 'Order' button does nothing because the main JavaScript thread is frozen during hydration.
2. The Architecture: Under 50 KB and 60 FPS
By returning to web fundamentals, we achieved blistering performance:
• Total Initial Payload: < 48 KB (gzipped JS + CSS bundle).
• First Contentful Paint (FCP): ~320 ms on real school 4G.
• Time to Interactive (TTI): Instantaneous, zero hydration lag.
The project structure remains clean, modular, and maintainable:
aksaramart/
├── public/
│ ├── index.html # Semantic HTML5 shell & modal templates
│ ├── skripmart.js # Modular Vanilla JS logic (~35 KB)
│ ├── stylemart.css # Clean CSS Variables design system
│ ├── sw.js # PWA Service Worker for offline asset caching
│ └── manifest.json # Progressive Web App configuration
3. Clean Patterns for Vanilla JS State & DOM
Building with Vanilla JS doesn't mean writing spaghetti code. We used modern ES6+ patterns, isolated state stores, and memory-efficient Event Delegation.
Pattern A: Modular State Store Without Third-Party Libraries
● ● ● JAVASCRIPT (SKRIPMART.JS)
// A lightweight, reactive-free state store (skripmart.js)
const appState = {
cart: JSON.parse(localStorage.getItem('aksara_cart') || '[]'),
products: [],
currentUser: null,
activeCategory: 'all'
};
function saveCart() {
localStorage.setItem('aksara_cart', JSON.stringify(appState.cart));
renderCartBadge();
}
function addToCart(productId) {
const product = appState.products.find(p => p.id === productId);
if (!product || product.stock <= 0) return;
const existing = appState.cart.find(item => item.id === productId);
if (existing) {
if (existing.qty < product.stock) existing.qty++;
} else {
appState.cart.push({ id: product.id, name: product.name, price: product.price, qty: 1 });
}
saveCart();
renderCartUI();
}
Pattern B: Memory-Efficient Event Delegation
Instead of binding individual click listeners to hundreds of menu cards (which bloats browser memory on low-end phones), we attach a single event listener to the parent container:
● ● ● JAVASCRIPT (SKRIPMART.JS)
// Only 1 single event listener for the entire product catalog grid
document.getElementById('catalogGrid').addEventListener('click', (e) => {
const btn = e.target.closest('[data-action="add-to-cart"]');
if (!btn) return;
const productId = btn.dataset.productId;
addToCart(productId);
// Smooth native CSS micro-animation without any external animation library
btn.classList.add('scale-bounce');
setTimeout(() => btn.classList.remove('scale-bounce'), 250);
});
4. Making It a True App: Progressive Web App (PWA) Standards
Most students won't download a canteen app from Google Play or Apple App Store due to limited phone storage. With PWA standards, the app installs instantly to their home screen directly from the browser in 1 tap.
a. Custom 'Install App' Topbar Trigger
We intercept the native beforeinstallprompt event to present a prominent, branded install button in the top navigation:
● ● ● JAVASCRIPT (PWA INSTALL)
let deferredPrompt;
const installBtn = document.getElementById('installAppBtn');
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault(); // Prevent default clunky mini-infobar
deferredPrompt = e;
if (installBtn) installBtn.style.display = 'inline-flex'; // Reveal our button
});
installBtn?.addEventListener('click', async () => {
if (!deferredPrompt) return;
deferredPrompt.prompt();
const { outcome } = await deferredPrompt.userChoice;
console.log(`User install outcome: ${outcome}`);
deferredPrompt = null;
installBtn.style.display = 'none';
});
b. Cache-First Service Worker (sw.js)
To ensure the catalog opens instantly even when students walk through hallways with dead Wi-Fi zones, the Service Worker caches all core app shell assets:
● ● ● JAVASCRIPT SERVICE WORKER (SW.JS)
const CACHE_NAME = 'aksaramart-v1.4';
const STATIC_ASSETS = [
'/',
'/index.html',
'/skripmart.js',
'/stylemart.css',
'/manifest.json',
'/icons/icon-192.png'
];
self.addEventListener('install', (e) => {
e.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
self.addEventListener('fetch', (e) => {
// Forward Supabase database REST & RPC calls directly to network
if (e.request.url.includes('/rest/v1/') || e.request.url.includes('/rpc/')) {
return;
}
// Serve static assets directly from local cache
e.respondWith(
caches.match(e.request).then(cached => cached || fetch(e.request))
);
});
5. The Performance Verdict: Real-World Lighthouse Audit
We executed Lighthouse audits on an emulated low-end Moto G4 device throttled to slow 3G network conditions:
6. Conclusion
Ditching frameworks for Vanilla JS proved to be a resounding success for our students and teachers. The app launches immediately, remains completely immune to hydration freezes, and preserves scarce phone battery and memory.
However, a fast frontend is completely meaningless if the underlying database is vulnerable. How do we secure transaction data from curious IT students who open Inspect Element to try altering menu prices from $1.50 to $0.01?
In the next article, we dive deep into our Zero-Trust security architecture using Supabase Row Level Security (RLS) and multi-role governance.


Top comments (0)