How to Migrate Legacy jQuery to Vanilla JavaScript
Migrating a legacy jQuery codebase to modern vanilla JavaScript improves performance, reduces bundle sizes, and future-proofs web applications without sacrificing functionality. This guide outlines a step-by-step strategy for executing an incremental transition, covering code audits, equivalent modern web APIs, dependency replacement, and testing methods to ensure your application remains stable throughout the process.
1. Audit the Codebase and Dependencies
Before writing code, inventory how jQuery is used across your project.
- Identify Third-Party Plugins: Search for plugins
like sliders, modals, or date pickers that rely directly on the jQuery
global (
$orjQuery). These require modern native alternatives or standard micro-libraries. - Determine Usage Scope: Check if jQuery is loaded
globally via a
<script>tag or bundled via npm/Webpack. - Assess Browser Support: Verify your project's browser support targets. Modern JavaScript (ES6+) and modern DOM APIs are fully supported across all modern browsers, making polyfills largely unnecessary unless legacy browsers like Internet Explorer are strictly required.
2. Adopt an Incremental Migration Strategy
A complete rewrite introduces high risk. Instead, migrate your codebase incrementally while running jQuery and vanilla JavaScript side-by-side.
- Leave the jQuery library loaded during development.
- Convert isolated features, utilities, or components one at a time.
- Use native JavaScript inside existing jQuery callbacks where
possible, progressively reducing calls to
$methods.
3. Replace Common jQuery Patterns with Native APIs
Modern browsers provide clean, native APIs that directly match jQuery’s functionality.
DOM Selection
Replace generic selector calls with targeted native methods:
- Single element:
- jQuery:
$('#container')or$('.item') - Vanilla JS:
document.querySelector('#container')ordocument.querySelector('.item')
- jQuery:
- Multiple elements:
- jQuery:
$('.item') - Vanilla JS:
document.querySelectorAll('.item')(returns aNodeListthat supports.forEach())
- jQuery:
DOM Manipulation
Standard DOM APIs provide direct methods to manipulate elements and classes:
- Class handling:
- Add:
element.classList.add('active') - Remove:
element.classList.remove('active') - Toggle:
element.classList.toggle('active')
- Add:
- Content:
- HTML:
element.innerHTML = '<span>Text</span>' - Text:
element.textContent = 'Text'
- HTML:
- Insertion and Removal:
- Append:
parent.append(child) - Prepend:
parent.prepend(child) - Remove:
element.remove()
- Append:
Event Handling
Replace .on(), .off(), and shorthand event
methods with standard event listeners:
- Basic listener:
// jQuery $('#btn').on('click', handler); // Vanilla JS document.querySelector('#btn').addEventListener('click', handler); - Event Delegation:
// jQuery $('#list').on('click', 'li', function(e) { /* ... */ }); // Vanilla JS document.querySelector('#list').addEventListener('click', (e) => { const target = e.target.closest('li'); if (target && e.currentTarget.contains(target)) { // Handle event with target } });
Asynchronous Requests
Replace $.ajax(), $.get(), and
$.post() with the native Fetch API:
// jQuery
$.ajax({
url: '/api/data',
method: 'GET',
dataType: 'json'
}).done(data => console.log(data));
// Vanilla JS
fetch('/api/data')
.then(response => {
if (!response.ok) throw new Error('Network error');
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error(error));4. Replace Legacy Plugins
Decouple jQuery-dependent UI widgets. For each plugin:
- Evaluate Native HTML Elements: Use modern native
elements such as
<dialog>for modals,<details>for accordions, or native input types (e.g.,type="date"). - Use Framework-Agnostic Packages: If a custom component is necessary, swap the jQuery plugin for a lightweight, dependency-free alternative available via npm.
- Write Custom Micro-Functions: Basic animations (e.g., slide down, fade in) can be handled using native CSS transitions triggered by toggling JavaScript classes.
5. Validate, Test, and Remove the Library
- Automated Testing: Run end-to-end (E2E) and integration tests across rewritten modules to catch regressions.
- Search for Stragglers: Use global project search
for
$andjQueryreferences to ensure no calls remain. - Remove the Source: Delete the jQuery
<script>tag or runnpm uninstall jquery. - Monitor Console Errors: Load the application and
verify that no
ReferenceError: $ is not definedmessages appear during standard user flows.