In modern web development, reaching for heavy frontend frameworks like React, Vue, or Angular has become a default response. While frameworks are excellent for massive, multi-team applications, they introduce hidden costs: large bundle sizes, complex runtime reconciliation, and high First Input Delay (FID) metrics on mobile viewports.

When we designed **CuriousCRM** (our flagship custom B2B CRM pipeline), performance was our top parameter. Business operations dashboards need to load instantly, handle rapid updates, and respond to drag-and-drop operations with zero latency. In this article, I will explain why we chose to build CuriousCRM using pure **Vanilla JavaScript** and how this choice yielded a faster, lighter application.

1. The Virtual DOM vs The Real DOM

React's core innovation is the Virtual DOM. When state changes, React builds a virtual tree, compares it to the previous state, and makes updates. While convenient, this diffing process consumes memory and CPU cycles.

In a CRM pipeline with hundreds of active leads in a Kanban board, drag-and-drop events trigger state recalculations dozens of times per second. In a Virtual DOM framework, dragging a card can trigger re-renders across all list columns, introducing slight lag (frame drops) on lower-end devices. With Vanilla JS, we target and update the specific DOM elements directly using native browser APIs, achieving **60fps animations** natively.

2. Leveraging Native HTML5 Drag and Drop

Instead of importing heavy external packages (like react-beautiful-dnd which adds ~50KB of code), we implemented the Kanban drag-and-drop functionality using the browser's native HTML5 Drag and Drop API:

// Configuring card drag listeners natively
const cards = document.querySelectorAll('.kanban-card');
const columns = document.querySelectorAll('.kanban-column');

cards.forEach(card => {
    card.addEventListener('dragstart', (e) => {
        e.dataTransfer.setData('text/plain', card.id);
        card.classList.add('dragging');
    });
    
    card.addEventListener('dragend', () => {
        card.classList.remove('dragging');
    });
});

columns.forEach(col => {
    col.addEventListener('dragover', (e) => {
        e.preventDefault(); // Required to allow dropping
    });
    
    col.addEventListener('drop', (e) => {
        const id = e.dataTransfer.getData('text/plain');
        const card = document.getElementById(id);
        col.querySelector('.cards-container').appendChild(card);
        // Execute background database sync
        updateLeadStage(id, col.dataset.stage);
    });
});

3. Zero Bundle Bloat

The average React application starts with a base bundle size of over 100KB before any application logic is written. When you add state managers, router libraries, and drag-and-drop scripts, the payload quickly exceeds 300KB.

For CuriousCRM, our entire codebase—including CRM logic, charts, Kanban boards, and database listeners—is **under 20KB in total**. It parses and executes in milliseconds, leading to a perfect 100/100 score on Google Lighthouse page speed audits.

4. Drawing Metrics Natively

To display sales indicators and pipeline reports, we utilized a lightweight library, `Chart.js`, directly in the script, avoiding complex framework wrappers. By directly passing data arrays into the canvas element, we minimized update overhead and avoided visual layout shifts (CLS).

Conclusion: Pick the Right Tool for the Job

Our decision to build CuriousCRM with Vanilla JS wasn't about being old-fashioned; it was a conscious performance choice. By avoiding heavy framework layers, we built a CRM dashboard that loads instantly, responds to user actions in microseconds, and remains completely maintainable with a small footprint. For interactive, dashboard-focused tools, sometimes the best framework is no framework at all.

Previous Story
Integrating Voice-Recognition Automation with Gemini API