I built 36 mental health tools using vanilla JavaScript. No React. No Vue. No framework. No build step. No npm install.
Every time I tell a developer this, I get the same look: why?
Here's the honest post-mortem.
The Context
I was building CBT (Cognitive Behavioral Therapy) tools — thought record apps, cognitive distortion detectors, core belief drills, safety behavior trackers. Each tool is small (50-200 lines of JS), runs entirely in the browser, and stores data in localStorage.
The tools live at github.com/alexcoledev/cbt-toolkit — 36 of them, all free, all open source.
Why I Chose Vanilla JS
1. Privacy is the product
Mental health data is the most sensitive data a person can have. My tools never send anything to a server. No analytics. No telemetry. No "anonymous" tracking.
A React app with a build step means:
-
npm installpulls 500+ dependencies - Any one of them could exfiltrate data
- The supply chain is an attack surface
Vanilla JS has zero dependencies. Zero supply chain. Zero attack surface. The code you read is the code that runs.
2. The DOM is not that complicated
Each tool has maybe 3-5 interactive elements. A thought record has a text area, a dropdown, a save button, and a list of past records.
This is not a SPA. This is a form with a list. React's virtual DOM is solving a problem I don't have.
// The entire "framework" for most tools:
function saveThought() {
const thought = document.getElementById('thought').value;
const records = JSON.parse(localStorage.getItem('records') || '[]');
records.push({ thought, date: new Date().toISOString() });
localStorage.setItem('records', JSON.stringify(records));
renderRecords();
}
No useState. No useEffect. No re-renders. No prop drilling.
3. Accessibility doesn't need a framework
Mental health tools serve users in distress. They need to work with screen readers, on old phones, on slow connections, with JavaScript partially disabled.
Vanilla JS HTML works with all of these by default. A <form> with a <button> works without JavaScript. React apps need the JS bundle to load before anything is interactive.
4. Reading the code is the documentation
Every tool is a single HTML file. Open it, read it, understand it. A therapist with basic HTML knowledge can verify the tool does what it claims. No webpack config. No JSX transformation.
This matters for mental health tools. If a therapist is going to recommend a tool to a patient, they should be able to audit it.
The Honest Trade-offs
I'm not going to pretend vanilla JS is all upside. Here's what I lost:
No component reuse
I have 36 tools. Many share patterns. In React, I'd have reusable components. In vanilla JS, I copy-paste. Each tool is self-contained.
But: the duplication is ~50 lines per tool. For 36 small tools, self-contained files are actually easier to maintain than a component tree.
No hot reload
When I change a tool, I refresh the page. No HMR.
But: the page loads in 50ms because there's no bundle. Refresh is faster than HMR.
Manual DOM updates
No reactive state. When data changes, I call render() manually.
But: for a tool with 3-5 elements, this is one function call. React's reconciliation algorithm is overkill.
When React WOULD Have Been Better
I'm not a zealot. React would have been better if:
- I had a team. Onboarding to 36 vanilla JS files is harder than a React component tree.
- I needed complex state. If tools shared state, React's context + reducers would help.
- I was building a product. If this was a startup, the React ecosystem would accelerate development.
- I needed SSR/SEO. Next.js would be the right choice for landing pages with SEO.
The Real Question
The real question isn't "React or vanilla JS?" It's: what does your problem actually require?
My problem: 36 small independent tools, maximum privacy, maximum auditability, maximum accessibility, no backend.
React solves problems I don't have while creating problems I don't want (build complexity, supply chain risk, bundle size, audit opacity).
The Results
- 445 clones, 157 cloners, 2 GitHub stars
- Zero security vulnerabilities reported (zero dependencies = zero CVEs)
- Each tool loads in <50ms
- Total codebase: ~15KB per tool, no node_modules
Would React have gotten more stars? Maybe. Would the tools be better? I don't think so. Would they be more private? Definitely not.
The takeaway: Frameworks are tools, not religions. Choose based on your problem, not your preference. For 36 privacy-first, therapist-auditable, zero-dependency mental health tools, vanilla JS was the right call.
If you want to see the tools: github.com/alexcoledev/cbt-toolkit
If you disagree, I'd love to hear why in the comments. Especially if you've built similar tools with React — I genuinely want to know what the trade-offs look like from the other side.
Top comments (0)