Maybe You Only Need Vanilla JavaScript: Challenging the Framework-First Mindset
Have you ever reached for create-react-app to build a simple counter? Or spun up Next.js for a page with three buttons? If you're nodding your head, you're not alone. Somewhere between 2015 and now, we collectively decided that frameworks aren't just useful, they're mandatory.
Note: This article is based on my presentation at DevFest Ibadan 2025, hosted by Google Developer Group Ibadan on November 1, 2025, What started as a 45-minute talk about challenging our framework-first assumptions has been expanded here with additional examples, deeper technical explanations, and code you can actually run. If you were in the audience that day, thank you for the great questions. If you weren't, welcome to the conversation.
But what if I told you that for a significant portion of what we build, we're shipping 120KB of abstraction to solve problems we don't actually have?
Let me be clear upfront: This isn't an anti-framework rant. React, Vue, and Angular exist for excellent reasons, and we'll talk about those reasons honestly. But the pendulum has swung so far toward "framework-first" thinking that we've forgotten to ask a simple question: Do I actually need this?
Today, we're challenging that default assumption. We'll compare vanilla JavaScript and React side-by-side, peek under the hood at what frameworks actually do, and most importantly, learn when each tool makes sense.
And if you're in Nigeria (or anywhere with expensive data and unreliable internet), this conversation isn't academic. Every kilobyte you ship costs your users money and patience.
Let's dig in.
What is "Vanilla" JavaScript Anyway?
The first time I heard "vanilla JavaScript," I was genuinely confused. Is this a different language? A library I missed?
Think of vanilla ice cream. It's the base flavor, no toppings, no mix-ins, just pure ice cream. Other flavors are built on top of it:
Chocolate = Vanilla + Cocoa
Strawberry = Vanilla + Strawberries
Mint Chip = Vanilla + Mint + Chocolate chips
Vanilla JavaScript = JavaScript without any additions. No React. No Vue. No frameworks, no libraries. Just the language as it exists in the browser.
A quick history note: JavaScript was created by Brendan Eich at Netscape in 1995, reportedly in just 10 days. It was originally called Mocha, then LiveScript, before becoming JavaScript. The problem it solved was simple but crucial: websites needed interactivity without full page reloads. Before JavaScript, every filter, every form submission, every tiny interaction required a server round-trip.
JavaScript has three parts:
The Language: Syntax, operators, data types, functions, objects, classes
The Runtime APIs: Browser APIs (DOM, Fetch, localStorage) or Node.js APIs (fs, http, process)
The Ecosystem: npm packages, build tools, developer tools, community
When we talk about "Vanilla JavaScript," we're talking about using parts 1 and 2 without adding layers from part 3.
The Golden Age Before Frameworks
Before React arrived in 2013 (public release in 2015), and before Angular came even earlier, building with vanilla JavaScript had some genuinely beautiful qualities:
1. Tiny Footprint
You didn't need to import anything. Write HTML, add a <script> tag, start coding. Zero bytes of dependencies.
2. Zero Build Time
No webpack. No Babel. No "waiting for the build to finish." You wrote code, hit refresh, and saw results instantly.
3. Simple Deployment
Remember those hosting platforms where you just upload a ZIP file? You'd create index.html, link your CSS and JS files, upload everything, and you were done. No build servers, no CI/CD pipelines, no deployment configurations.
4. No Framework/Vendor Lock-In
Your code was yours. If a new tool came along, you could adopt it gradually. You weren't married to React's release schedule or Vue's breaking changes.
5. Lightweight Dev Environment
These days, dev servers can take 10 minutes to reload on large React apps. You make a change, go get water, come back, and maybe it's finished recompiling. With vanilla JS? Instant feedback. Change file, refresh browser, done.
This sounds like paradise, right?
Why Frameworks Won (And Why That Made Sense)
Frameworks didn't take over because developers are sheep who follow trends. They took over because they solved real, painful problems:
1. Cross-Browser Nightmare
Different browsers implemented JavaScript features differently. Getting user location? Different APIs. CSS animations? Special code for Internet Explorer. Event handling? Safari did it differently than Chrome. You'd write one version for modern browsers, another for IE, and pray it worked on mobile.
Frameworks with bundlers like webpack and Babel would transpile your code to work everywhere. Write once, run anywhere.
2. Manual DOM Hell
Let's say you're building a counter. In vanilla JS, you had to:
function increment() {
count++; // Data changed
document.getElementById('display').innerHTML = count; // Manually update UI
document.getElementById('badge').innerHTML = count; // Manually update UI again
document.getElementById('sidebar').innerHTML = count; // And again...
document.getElementById('btn').disabled = count >= 10; // Don't forget this one!
// Easy to forget one. Bugs everywhere.
}
Every single place that displays the count needs manual updating. Miss one? Your UI desyncs from your data. This is tedious and error-prone.
3. Harder Reusability / More Complexity for Reuse
Want to reuse a component? In vanilla JS, you'd have to manually structure functions, manage HTML templates, handle initialization. There was no standard component model.
4. State Chaos - Global Variables and Collisions
You'd import different script files, and if two scripts happened to use the same variable name, you were in trouble. No encapsulation meant collision risks everywhere.
React components are enclosed. Everything you define inside a component stays inside it.
5. Untestable Code - Tightly Coupled, Harder to Maintain
Look at this code:
document.getElementById('balance').textContent = patricia.getWalletBalance();
This is tightly coupled to both the HTML (via that specific ID) and the Patricia API. If Patricia shuts down tomorrow (like they did recently), you have to find every single line that references Patricia. If someone changes that balance ID in the HTML, this breaks silently.
Better practice? Write a facade:
function getWalletBalance() {
return patricia.getWalletBalance(); // Can easily swap to Flutterwave
}
But vanilla JS didn't enforce these patterns. Frameworks did.
What React/Vue/Angular Actually Gave Us
Frameworks earned their dominance by providing five critical improvements:
1. Component Reusability
Write a button component once, use it everywhere. Clear, modular, maintainable.
2. Declarative UI
Instead of saying "find this element, change its text, update this other element," you say "when count is 5, the UI looks like this." You describe what should be rendered, not how to update it.
3. Reactive Data Binding
Change the data, UI updates automatically. No manual syncing.
4. Rich Ecosystems
Thousands of libraries, established patterns, battle-tested solutions. Need routing? There's a library. Need state management? Multiple options.
5. Team Alignment
Everyone knows React. Hiring is easier. Onboarding is faster. "Let's build this in React" is a no-brainer for team coordination.
These are real benefits. Frameworks won for good reasons.
But Everything Has a Cost
Here's the part we don't talk about enough. When you choose a framework, you're not just gaining features. You're accepting trade-offs. And it's not just you as a developer who pays, your users, your team, and your company all bear the costs.
The Trade-offs We Accepted:
1. Bundle Size Bloat
That simple counter app? If you build it with React, you're shipping ~50-120KB of React library code just to count clicks. For users on slow networks or expensive data plans, that's pain.
2. Build Complexity
Most frameworks needs to be compiled, transpiled, bundled. Every change requires a build step, except you use the CDN versions (Content delivery network)
3. Framework Lock-In
If React is sunsetted tomorrow, you're in trouble. Companies literally pay framework maintainers to keep building because they know if development stops, their apps are stuck. Facebook gave React $1.5-2.5 million recently to ensure continued development.
4. Abstraction Layers
React hides how the DOM actually works. If you only know React, you don't know getElementById vs querySelector performance differences, or which DOM APIs are fast vs slow.
5. Easier to "Mess Things Up"
With React, you start importing libraries for everything. Form validation? Import a library. State management? Import Redux. Before you know it, you have 47 dependencies to do things that are built into the browser.
I once saw an engineer spend two days trying to pause background audio when users scrolled away in a TikTok-style app. He was managing Redux state, tracking which audios were playing, dispatching pause actions...
The vanilla solution? document.querySelectorAll('audio').forEach(audio => audio.pause()). Done.
6. Long-Running Servers
With Server-Side Rendering (SSR), Static Site Generation (SSG), and other modern framework features, you now need servers that actively process requests. This is different from just serving static HTML files. Your hosting costs go up.
7. Hosting/Cloud Costs
SSR, incremental regeneration, edge functions, all these cost money. Vercel, Netlify, and others make their money here.
8. Time
Every library you add increases load time. For users in poor network coverage areas, this isn't theoretical—it's the difference between your app loading or not.
9. Trickle-Down Panic When Things Go Wrong
Remember the npm supply chain attacks a few months ago? Libraries got hacked, scripts started reading .env files and uploading secrets to remote servers. If you're using vanilla HTML and CSS, you're not running npm install and you wouldn't have been affected.
Axios had a security vulnerability six months ago that could let attackers manipulate API requests. If you're on an old version and don't know, you're exposed.
10. Cognitive Complexity for New Engineers
You just learned JavaScript. Variables return strings, numbers, functions. Now someone shows you React and you see return <div>Hello</div>. Wait, we're returning HTML? And why is it className instead of class? There's a learning curve just to understand the abstractions.
Feature Showdown: Reactivity & State
Let's get concrete. I'm going to show you the same app built two ways, and we'll compare them across eight dimensions.
The Challenge
Build a car counter app. You're standing by a roadside counting BMWs and Mercedes-Benz cars that pass by. The app needs:
A button for BMW (increments BMW count)
A button for Benz (increments Benz count)
Display total count (sum of both)
All displays update automatically
Simple, right? Let's see both approaches.
The Vanilla JS Approach
let benzCount = 0;
let totalCount = 0;
function incrementBenzCount() {
benzCount++; // Data changed...
totalCount++; // Another data changed...
document.querySelector('.benz-count').textContent = benzCount; // But we manually update UI
document.querySelector('.total-count').textContent = totalCount;
// Imagine doing this everywhere...
}
What happens:
Increment the
benzCountvariableIncrement the
totalCountvariableFind the
.benz-countelement in the DOMUpdate its text content
Find the
.total-countelement in the DOMUpdate its text content
Four lines of code inside the function, two variables declared outside. And this is just for one button. The BMW button needs the same treatment.
You can wrap the querySelector calls in a helper function to reduce repetition:
function updateContent(selector, value) {
document.querySelector(selector).textContent = value;
}
function incrementBenzCount() {
benzCount++;
totalCount++;
updateContent('.benz-count', benzCount);
updateContent('.total-count', totalCount);
}
Slightly cleaner, but still the same four lines of logic.
The React Approach
const [benzCount, setBenzCount] = useState(0);
const [bmwCount, setBmwCount] = useState(0);
const totalCount = benzCount + bmwCount; // Derive data automatically
function incrementBenzCount() {
setBenzCount(benzCount + 1); // Just update data
}
What happens:
Call
setBenzCount(benzCount + 1)React handles everything else—UI updates automatically
Notice something crucial: totalCount isn't a separate state. It's derived state, calculated from other state. This is a React best practice. Don't create redundant state; derive it.
Deep Dive: The 8 Dimensions of Comparison
Let's analyze these two approaches across multiple dimensions.
1. Declarative vs Imperative
React (Declarative):
const totalCount = benzCount + bmwCount; // Declare relationship once
// UI automatically updates when dependencies change. You describe WHAT, not HOW.
Vanilla (Imperative):
benzCount++;
updateContent('.benz-count', benzCount); // Manual sync
// You explicitly tell the DOM what to do, every single time. Easy to forget steps.
React lets you declare "total count equals the sum of these two values," and it handles keeping the UI in sync. Vanilla requires you to manually execute the sync steps each time.
2. Cognitive Load
React: Lower
You think about state, not DOM manipulation. Mental model = "data flows down." Change the data, React handles the rest.
Vanilla: Higher
You must remember: (1) update variable, (2) find DOM node, (3) update it. Three-step process for every change. Easy to desync if you miss a step.
3. Bug Probability
In programming, more code = more potential bugs. If you write 10 lines, you can introduce up to 10 bugs (one per line). If you write 100 lines, up to 100 bugs.
React:
Fewer steps = fewer bugs. You change state, React handles rendering.
Vanilla:
More steps = more opportunities for errors. Forgot to update one display? Typo in a selector? Wrong element ID? Each is a potential bug.
4. Memory Footprint
React:
~50-120KB (gzipped) for the React library
Virtual DOM (~3x actual DOM size in memory)
Heavy upfront cost
Vanilla:
~0KB overhead. Just your variables. The JavaScript engine is already in the browser—you don't import anything.
The catch: For small apps (<10KB), React's overhead is significant. For large apps (>5MB), React's 120KB becomes negligible as a percentage of total size.
5. Execution Speed (Runtime)
React:
Slower per update. State change → diffing algorithm → batch updates → DOM patch = ~1-5ms per update cycle.
Vanilla:
Faster per update. Direct DOM manipulation = ~0.1ms per update.
The catch: For 1-100 updates, the difference is negligible. For 10,000 updates/second (like a data visualization or game), vanilla wins.
6. What Happens Behind The Hood
React:
Triggers re-render
React diffs the Virtual DOM against the previous version
Batches changes together
Updates only the changed DOM nodes
Calls useEffect hooks
Vanilla JS:
querySelectorfinds the nodeSets
textContentdirectlyDone
React does more work, but that work provides benefits: batching prevents multiple repaints, diffing ensures minimal DOM changes.
7. State Consistency
React:
Single source of truth. The state (benzCount) is the source of truth. UI always matches state (eventually). Impossible to desync.
Vanilla:
Dual sources of truth. Is the truth the benzCount variable? Or the DOM? Or both? If you update the variable but forget to update the DOM, they're out of sync.
8. Memory Leaks
React:
Rare. Cleanup happens automatically when components unmount (unless you misuse useEffect by forgetting to return a cleanup function).
Vanilla:
Common. Forgot to remove an event listener? Memory leak. You declared a variable and attached an event listener:
const name = "feranmi"; // Compilation assigns memory block "1101"
document.addEventListener('click', handler); // Assigns another memory block
// If you never remove this listener, the memory stays occupied forever
When JavaScript compiles your code, it assigns memory addresses to variables. Let's say name gets memory address 1101. That address is marked as "occupied" and stores the value "feranmi".
If you never clean up (by removing event listeners or clearing variables), those memory blocks stay occupied even after you're done with them. The browser's garbage collector can't reclaim them because they're still referenced.
React manages this automatically. When a component unmounts, React cleans up event listeners and state. With vanilla JS, you're responsible for cleanup.
But Wait—You CAN Do Reactivity in Vanilla
Everything I just showed you about React's reactivity? You can build it yourself in vanilla JavaScript in about 10 lines of code.
Meet JavaScript Proxies.
Building Your Own useState
// 1. Create reactive state
function createReactiveState(initialState, callback) {
return new Proxy(initialState, {
set(target, property, value) {
target[property] = value;
callback(); // Notify on change
return true;
}
});
}
// 2. Use it
const state = createReactiveState(
{ count: 0 },
() => render()
);
// 3. Render function (template literal)
function render() {
const { count } = state;
document.querySelector('#app').innerHTML = `
<p>Count: ${count}</p>
<p>Double: ${count * 2}</p>
<p>Status: ${count > 5 ? 'High' : 'Low'}</p>
<button onclick="state.count++">+</button>
`;
}
// Initial render
render();
What happens:
State Proxy: Wraps your state object in a Proxy that intercepts changes
Usage: When you do
state.count++, the Proxy'ssettrap firesTrigger Rendering: The callback (
render()) is called automatically
You've just built React's useState in vanilla JavaScript.
Important Caveat
This isn't production-ready. To truly replicate React, you'd need to add:
Batching updates (React doesn't re-render 10 times if you change 10 state values; it batches them)
Diffing algorithm (React only updates changed DOM nodes, not the entire tree)
Lifecycle hooks (equivalent to
useEffect)Memory leak prevention (cleanup when components unmount)
But the core concept? You just built it. React isn't magic, it's JavaScript using browser APIs cleverly.
Feature Showdown: Components & Reusability
One of the biggest selling points for frameworks is component reusability. Let's see how both approaches handle this.
The Challenge
Build a <ProductCard> component that displays:
Product image
Product name
Product price
"Add to Cart" button
The React Way
function ProductCard({ product, onAddToCart }) {
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p className="price">${product.price}</p>
<button onClick={() => onAddToCart(product)}>
Add to Cart
</button>
</div>
);
}
// Usage
<ProductCard product={product} onAddToCart={handleAdd} />
Clean, familiar, composable. This only works in React.
The Web Components Way
class ProductCard extends HTMLElement {
connectedCallback() {
const product = JSON.parse(this.getAttribute('product'));
this.innerHTML = `
<div class="product-card">
<img src="${product.image}" alt="${product.name}" />
<h3>${product.name}</h3>
<p class="price">$${product.price}</p>
<button class="add-to-cart">Add to Cart</button>
</div>
`;
this.querySelector('.add-to-cart').addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('add-to-cart', {
detail: product
}));
});
}
}
// Register the component
customElements.define('product-card', ProductCard);
Usage:
<product-card product='{"name":"Phone","price":299,"image":"..."}'></product-card>
<script>
document.querySelector('product-card')
.addEventListener('add-to-cart', (e) => {
console.log('Added:', e.detail);
});
</script>
What Are Web Components?
Web Components are a set of web platform APIs that let you create new custom, reusable, and encapsulated HTML tags for use in web pages and web applications.
They're based on existing web standards and work across modern browsers, offering a way to extend HTML with new elements that have custom behavior and encapsulated styling.
Key features:
Custom HTML tags like
<product-card>Shadow DOM for style encapsulation (styles don't leak out, outside styles don't leak in)
Works everywhere: React, Vue, Angular, or vanilla JS
Zero dependencies (browser built-in)
Perfect for design systems & micro-frontends
The Advantage
React components only work in React.
Web Components work everywhere.
Let's say you're building a design system for a company that has multiple teams using different frameworks. Team A uses React. Team B uses Vue. Team C uses Angular.
If you build components in React, you need to maintain three separate versions. Every time you update a button style, you update it in three places.
If you build with Web Components, you write it once. All teams import the same component. When React 19 comes out with breaking changes, your Web Components still work. When Vue 4 launches, your components still work.
You're not locked to any framework's release schedule.
Feature Showdown: Client-Side Routing
Frameworks like react-router, vue-router, and angular-router make routing seem like magic. But what are they actually doing?
How Framework Routers Work
Listen for the
popstateevent (fires when URL changes via back/forward buttons)Check the routes list (map of paths to components)
Render the correct component for the current path
Cleanup and wait for the next route change
That's it. The "magic" is just event listeners and conditional rendering.
How to Implement Your Own Router
// 1. Define routes
const routes = {
'/': renderProductsPage,
'/cart': renderCartPage,
};
// 2. Router handler
function router() {
const path = window.location.pathname;
const render = routes[path] || render404;
render();
}
// 3. Navigate utility
function navigate(path) {
history.pushState(null, null, path);
router();
}
// 4. Handle back/forward
window.addEventListener('popstate', router);
// 5. Intercept clicks
document.addEventListener('click', (e) => {
if (e.target.matches('a[href^="/"]')) {
e.preventDefault();
navigate(e.target.getAttribute('href'));
}
});
// Initial render
router();
What happens:
Routes definition: Map paths to render functions
Router handler: Looks at current URL, calls appropriate render function
Navigate utility: Programmatically change routes via
history.pushState()popstate listener: Handles back/forward button clicks
Click interceptor: Catches link clicks and uses
navigate()instead of full page reload
You've just built client-side routing in ~20 lines.
Why It's Called "Client-Side"
Routing used to be a server thing. If you visited /products, the server returned a products page. If you visited /cart, the server returned a cart page. Every route change = full page reload.
Client-side routing moves this to the browser. The server sends all the JavaScript once. Then the JavaScript handles routing without talking to the server again.
That's why apps using react-router feel fast—no page reloads, just JavaScript swapping content.
What You Gain By Going Vanilla
When you deeply understand vanilla JavaScript, you gain five things:
1. Deep Platform Understanding
You know how things actually work. You understand getElementById is faster than querySelector. You know which DOM APIs are O(n) vs O(1). You understand browser repaints and reflows.
This makes you a better developer even when using frameworks because you understand what's happening under the abstractions.
2. Zero Framework Churn / Framework Lock-In
No anxiety about React 19 breaking changes. No scrambling when a framework is deprecated. Your skills transfer across tools because you understand the foundation.
3. Performance by Default
The browser is already optimized for JavaScript and DOM manipulation. You get fast performance without needing to "optimize your bundle" or "code-split properly."
4. More Control
You decide exactly what happens, when, and how. No framework making decisions for you.
5. Future-Proof (Work AND Yourself)
Your code will keep working. Browsers are backward-compatible. Code you write today in vanilla JS will work in browsers 10 years from now.
And you are future-proof. In an AI world where tools can generate boilerplate React code, your deep understanding of fundamentals, memory leaks, event loops, DOM performance, reactivity patterns, is what makes you valuable.
The Point (Because We Need to Say It Clearly)
If you take away three things from this article, let them be:
1. Understand the Whole Spectrum
Don't just learn React. Learn JavaScript deeply. Understand what React does under the hood. Know when frameworks add value and when they add bloat.
2. Choose Based on Project Needs
Not every project needs React. A landing page with three buttons? Vanilla JS. A complex dashboard with real-time data and 50 interactive components? React makes sense.
3. Don't Default to React for Everything
Break the reflex. When starting a new project, ask: "Do I actually need a framework for this?" Sometimes the answer is yes. But often, it's no.
When to Actually Use Vanilla JS
Here are five scenarios where vanilla JavaScript is the right choice:
1. Small/Low-Complexity Projects
If your project is under 10-20 components or has minimal interactivity, vanilla JS will be faster to write, faster to load, and easier to maintain.
Example: A personal portfolio site, a documentation page, a simple calculator.
2. Performance Optimization Matters
If you're building for users on slow networks or expensive data plans (hello, Nigeria), every kilobyte matters. Vanilla JS has zero overhead.
Example: A mobile app for emerging markets, an offline-first PWA.
3. Learning Fundamentals
If you're still grasping how JavaScript works, build projects in vanilla first. Understand closures, scope, async/await, DOM manipulation, event delegation. Then learn React.
You'll appreciate what frameworks do because you'll know what problems they solve.
4. You Need More Control
Sometimes you need precise control over rendering timing, event handling, or memory management. Frameworks make decisions for you. Vanilla JS lets you decide.
Example: A data visualization library, a game engine, performance-critical animations.
5. Speed for Light Projects
For throwaway prototypes, internal tools, or quick demos, vanilla JS is faster. No npm install, no build step, no framework decisions. Just code.
The Nigerian Context: Why This REALLY Matters
Let me bring this home.
In Nigeria, where:
Data costs money (1GB can cost ₦500-1000, and many people are on prepaid)
Internet can be slow (3G is common, 4G is unreliable, 5G is rare)
Users are patient only so long (if your app doesn't load in 5 seconds, they'll switch to a competitor)
Every kilobyte matters.
When you ship a React app that's 2MB after "optimization," you're asking Nigerian users to:
Pay for that data
Wait for it to download over a slow connection
Hope their connection doesn't drop mid-download
This isn't hypothetical. I've watched people close apps because "this one is not working" when really it's just... still loading.
A vanilla JS app that's 50KB loads fast, costs less data, and works on terrible connections.
Choose wisely.
Your technical choices have real-world consequences for real people. A smaller bundle isn't just a performance metric—it's respect for your users' constraints.
Final Thoughts: The Balanced Take
Let's bring this full circle with five closing points:
1. Frameworks Exist for Good Reasons
React, Vue, and Angular solved real problems: cross-browser compatibility, manual DOM updates, state management chaos, component reusability. They earned their dominance.
2. But They're Not Always the Answer
For small projects, simple interactions, or performance-critical apps, frameworks are often overkill. The overhead isn't worth it.
3. JavaScript is Powerful on Its Own
With Proxies, Web Components, and modern DOM APIs, vanilla JavaScript can do a lot. You can build reactivity, routing, and components without frameworks.
4. Small Bundles = Happy Users = Successful Businesses
Users on slow networks or expensive data plans will love you for keeping your app lightweight. Happy users stick around. Happy users tell friends. Happy users convert.
5. Understanding Fundamentals Makes You a Better Developer
Whether you end up using React or vanilla JS, understanding how things actually work makes you more effective. You debug faster. You optimize better. You make smarter architectural decisions.
In an era where AI can generate boilerplate framework code, your deep understanding of fundamentals is what makes you irreplaceable.
Skilling Up: Where to Learn More
Want to dive deeper into vanilla JavaScript? Here are the best resources:
Books & Websites:
O'Reilly Books: "JavaScript: The Definitive Guide," "You Don't Know JS" series
Web.dev: Google's web development resource with excellent JavaScript guides
Mozilla Web Docs (MDN): The definitive reference for JavaScript, DOM APIs, Web Components
Courses:
- Frontend Masters: Beginner to advanced JavaScript courses, including deep dives into Proxies, Shadow DOM, and Web APIs
Topics to Master:
JavaScript Proxies
Shadow DOM
Web Components
DOM APIs (and their performance characteristics)
Event delegation
Memory management and garbage collection
Conclusion: The Real Framework is Understanding
I started learning to code by accident. I had an itel phone, installed a random app, and started writing HTML without even knowing it was code. I built a CBT app using link tags and thousands of <br> tags to "jump" between questions.
It was ridiculous. It was clever. It worked.
When I finally learned JavaScript, I didn't start with React. I used alert(), confirm(), and querySelector. Every line of code taught me something about how the web works.
That foundation, understanding the platform deeply, is what makes me effective today. Whether I'm writing React, Angular, or vanilla JavaScript, I know what's happening under the hood.
The best framework isn't React or Vue or Angular.
The best framework is understanding.
So before you type npx create-react-app for your next project, pause. Ask yourself: Do I actually need this?
Sometimes the answer is yes. But often, more often than we admit, the answer is no.
And in those moments, vanilla JavaScript is enough.
**Choose wisely. Every kilobyte matters.**Resources from the Talk
Slides: You can view the original presentation slides from DevFest Ibadan here


