The Framework Trap
Every developer has been there. You start a new project and immediately reach for your favorite framework:
npx create-react-app my-calculator
# Installing 1,453 packages...
# 3 minutes later...
# node_modules folder: 289 MB
Three minutes and 289 MB later, you have a "Hello World" that takes 2 seconds to load on 3G.
For a calculator.
When we started building ToolsForIndia.com, we almost fell into the same trap. But then we asked a simple question:
"Do we actually need React for this?"
Spoiler: We didn't. And going vanilla JavaScript made everything faster, simpler, and more maintainable.
The Requirements: What We Actually Needed
Before choosing our tech stack, we listed our actual requirements:
Must Have:
✅ Fast load times (< 1 second on 3G)
✅ Client-side calculations (privacy)
✅ Export to PDF/CSV
✅ URL parameter sharing
✅ Mobile responsive
✅ Works offline (after first load)
Nice to Have:
✅ Charts and visualizations
✅ Dark mode (future)
✅ Multiple language support (future)
Don't Need:
❌ Real-time collaboration
❌ Complex state management
❌ Server-side rendering
❌ Database integration (yet)
❌ User authentication (yet)
Verdict: None of our "must haves" require a framework. We were about to add 289 MB of dependencies for features we didn't need.
The Stack: Intentionally Simple
Here's our entire tech stack:
<!DOCTYPE html>
<html>
<head>
<!-- Tailwind CSS via CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Chart.js for visualizations -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script>
<!-- jsPDF for exports -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
</head>
<body>
<!-- Pure HTML -->
<div id="calculator">
<!-- No JSX, no virtual DOM, just HTML -->
</div>
<script>
// Pure JavaScript
// No webpack, no babel, no build step
</script>
</body>
</html>
Total bundle size: ~200 KB
Load time on 3G: < 1 second
Build time: 0 seconds (no build step!)
Architecture Decisions: Why Each Choice
1. Vanilla JavaScript Over React
React Approach:
// React component
import React, { useState, useEffect } from 'react';
function Calculator() {
const [amount, setAmount] = useState(0);
const [result, setResult] = useState(0);
useEffect(() => {
calculateResult();
}, [amount]);
return (
<div>
<input onChange={(e) => setAmount(e.target.value)} />
<p>Result: {result}</p>
</div>
);
}
Advantages:
✅ No framework overhead
✅ No virtual DOM reconciliation
✅ Direct DOM updates (faster for simple UIs)
✅ No JSX compilation needed
✅ Anyone can read the code (no framework-specific patterns)
When React Would Win:
✅ Complex state management
✅ Many component re-renders
✅ Deeply nested component trees
✅ Team already knows React
For calculators? Vanilla wins.
2. Tailwind CSS via CDN (Not Compiled)
Controversial choice. Every Tailwind expert will tell you to use PostCSS and PurgeCSS to remove unused classes.
We use the full CDN version.
Why?
<!-- Our approach: One CDN link -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- 50 KB gzipped, cached across sites -->
<!-- "Proper" approach: -->
<!-- Install Node.js, npm, PostCSS, PurgeCSS -->
<!-- Configure build pipeline -->
<!-- Run build on every change -->
<!-- Get 8 KB CSS file -->
Trade-off: We ship 42 KB extra CSS that's not used.
Gain: Zero build complexity, instant development, CDN caching.
For tools with < 10,000 daily users, this is the right trade-off.
When Compiled Tailwind Wins:
- High traffic (millions of users)
- Every KB matters
- Have dedicated frontend team
- Can maintain build pipeline
3. Client-Side Calculations Only
Every calculation happens in JavaScript. Zero server calls.
// SIP Future Value calculation (runs in browser)
function calculateSIP(monthlyInvestment, returnRate, years) {
const months = years * 12;
const monthlyRate = returnRate / 12 / 100;
const futureValue = monthlyInvestment *
(Math.pow(1 + monthlyRate, months) - 1) /
monthlyRate *
(1 + monthlyRate);
return Math.round(futureValue);
}
// No API call, no server, no latency
Advantages:
✅ Instant results (no network latency)
✅ Privacy (data never leaves browser)
✅ Works offline (after first page load)
✅ Scales infinitely (no server costs)
✅ No backend maintenance
When Server-Side Wins:
✅ Need to persist data
✅ Complex calculations (> 5 seconds)
✅ Multi-user features
✅ Need to update logic without redeploying
For calculators? Client-side wins every time.
4. localStorage for State (Not Redux)
We save user scenarios to localStorage:
// Save calculation
function saveScenario(name, data) {
const scenarios = JSON.parse(localStorage.getItem('scenarios') || '[]');
scenarios.push({ name, data, timestamp: Date.now() });
localStorage.setItem('scenarios', JSON.stringify(scenarios));
}
// Load scenarios
function loadScenarios() {
return JSON.parse(localStorage.getItem('scenarios') || '[]');
}
No Redux. No Context API. No state management library.
Advantages:
✅ Persists across sessions
✅ No server storage needed
✅ Simple API (2 methods: getItem, setItem)
✅ 5-10 MB storage per domain
Limitations:
❌ Can't sync across devices
❌ User can clear it
❌ Not suitable for sensitive data
For saving calculator scenarios? Perfect fit.
5. URL Parameters for Sharing
Want to share a calculation? We encode it in the URL:
// Generate shareable URL
function generateShareURL() {
const params = new URLSearchParams({
monthly: document.getElementById('monthly').value,
years: document.getElementById('years').value,
rate: document.getElementById('rate').value
});
return `${window.location.origin}${window.location.pathname}?${params}`;
}
// On page load, read params
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('monthly')) {
document.getElementById('monthly').value = urlParams.get('monthly');
calculateResult(); // Auto-calculate
}
No database. No shortened URLs. Just URL parameters.
Advantages:
✅ Works immediately (no backend)
✅ Shareable via any medium
✅ Bookmarkable
✅ SEO-friendly (Google indexes different params)
Example:
toolsforindia.com/tools/sip-calculator.html?
monthly=10000&years=15&rate=12
Anyone clicking that link sees your exact calculation.
Performance: The Numbers
We ran Lighthouse audits on our calculators:
SIP Calculator:
Performance: 98/100
Accessibility: 95/100
Best Practices: 100/100
SEO: 100/100
Load Times (tested on 3G):
First Contentful Paint: 0.8s
Time to Interactive: 1.2s
Total Bundle Size: 187 KB
Compare to typical React app:
FCP: 2-3s
TTI: 3-5s
Bundle: 500KB - 2MB
Development Experience: Surprisingly Good
- No Build Step = Instant Feedback
- Change code → Refresh browser → See result
- No waiting for webpack. No hot module replacement bugs. No build errors.
- Easy Debugging
// Set breakpoint in browser DevTools
// See actual code, not transpiled/minified version
// Step through line by line
With frameworks, you're debugging transpiled code or source maps. With vanilla JS, you're debugging what actually runs.
Easy Onboarding
New developer joins? They need to know:
- HTML
- CSS
- JavaScript
- That's it
No React concepts. No webpack config. No babel presets.
The Trade-offs: What We Gave Up
1. Component Reusability
React components are easy to reuse. With vanilla JS, we copy-paste more.
Our solution: Keep components small and independent.
2. State Management at Scale
If we had 50+ interactive elements with complex dependencies, Redux would help.
Current reality: Each calculator has ~10 inputs. Vanilla JS handles this fine.
3. Developer Ecosystem
React has thousands of ready-made components (date pickers, charts, etc.).
Our solution: Use vanilla JS libraries (Chart.js, jsPDF) that don't require frameworks.
4. Testing
React has great testing tools (Jest, React Testing Library).
Our solution: Manual testing for now. Will add vanilla JS tests when needed.
When You Should Use a Framework
Don't take this as "frameworks are bad." Use them when:
✅ Building complex SPAs with many views
✅ Team already knows the framework
✅ Need component reusability at scale
✅ Complex state management (>50 pieces of state)
✅ Real-time features
✅ Need server-side rendering
For calculators, landing pages, blogs, small tools? Vanilla JS is often the better choice.
The Results: User Feedback
After 30 days:
Speed:
- "Finally, a calculator that loads instantly!"
- "Works great even on slow internet"
Privacy:
- "Love that it doesn't need signup"
- "Calculation happens in browser = my data stays private"
Simplicity:
- "Clean design, no clutter"
- "Just does what it says"
Lessons Learned
- Question Framework Defaults Just because everyone uses React doesn't mean you need it. Evaluate based on requirements, not trends.
- Optimize for Real Users Our users are in India, often on 3G. Fast load times > fancy animations.
- Ship First, Optimize Later We shipped with CDN Tailwind. If we hit 100K users, we'll compile it. Don't optimize prematurely.
- Constraints Breed Creativity No frameworks forced us to write cleaner, more intentional code.
The Code: Open for Inspection
Visit any calculator page, right-click, "View Page Source."
You'll see:
- Readable HTML
- Understandable JavaScript
- No minification (yet)
- No obfuscation
This is intentional. We want developers to learn from our code.
Try It Yourself
SIP Calculator: toolsforindia.com/tools/sip-calculator.html
PPF Calculator: toolsforindia.com/tools/ppf-calculator.html
Open DevTools. Check the Network tab. See the load times yourself.
Conclusion: Right Tool for the Job
React is amazing for what it does. But it's not always the right tool.
For ToolsForIndia.com, vanilla JavaScript gave us:
⚡ 5x faster load times
🔒 Better privacy (client-side only)
🧹 Simpler codebase
💰 Lower complexity cost
Would we use React for a complex dashboard with 100+ components? Absolutely.
For calculators? Vanilla JS wins.
About the Author
Building ToolsForIndia.com with intentionally simple tech. Previously worked with React/Vue/Angular. Now shipping faster with vanilla JS. Believes in using the simplest tool that solves the problem.
🔗 toolsforindia.com
🐦 @toolsforindia
📧 contact@toolsforindia.com
P.S. View source on our calculators. All code is unminified and readable. Learn, copy, improve.
Top comments (0)