The modern web development ecosystem loves dependencies. A typical React project starts with hundreds of packages in node_modules before you write a single line of your own code. Build tools, transpilers, bundlers, linters, type checkers -- the toolchain itself has become a project to maintain.
There is another way. For certain categories of web applications -- particularly single-purpose developer tools -- you can skip all of that and ship plain HTML, CSS, and JavaScript. No build step. No package.json. No dependency updates. I have been building tools this way for years, and the results speak for themselves.
Why Zero Dependencies
The argument for zero-dependency web tools comes down to three things: speed, longevity, and simplicity.
Speed. A static HTML file with inline JavaScript loads in milliseconds. There is no framework initialization, no virtual DOM diffing, no hydration step. The browser parses the HTML, runs the script, and the tool is ready. For a Base64 encoder or a URL decoder, this is all you need.
Longevity. This is the argument that rarely gets enough attention. A React app built in 2020 already needs significant updates to run with current tooling. A plain HTML file from 2020 still works exactly as it did the day it was written. Browsers are remarkably backward-compatible. The web platform is the most stable runtime in existence.
Simplicity. When everything is in one file, debugging is straightforward. There are no source maps to configure, no build artifacts to trace, no framework abstractions between your code and the browser. You open the file, you read the code, you understand what it does.
What the Browser Gives You for Free
Developers often reach for npm packages to solve problems that the browser already handles natively. Consider what is built into every modern browser:
btoa()andatob()for Base64 encoding and decodingencodeURIComponent()anddecodeURIComponent()for URL encodingcrypto.subtle.digest()for SHA-256, SHA-384, SHA-512 hashingcrypto.randomUUID()for generating UUIDsJSON.parse()andJSON.stringify()for JSON formattingnavigator.clipboardfor clipboard read/writeTextEncoder/TextDecoderfor text encodingfetch()for HTTP requestslocalStoragefor persistent client-side storage
Each of these is a native API with zero bytes of additional JavaScript to download. They are fast, well-documented, and maintained by browser vendors. For building developer tools, these APIs cover an enormous range of functionality.
The Structure That Works
After building over a dozen tools this way, a pattern has emerged that works well:
my-tool/
index.html <-- Everything: markup, styles, logic
favicon.ico
css/
styles.css <-- Shared theme CSS (Bootstrap + customizations)
js/
scripts.js <-- Shared theme JS (navbar, smooth scroll)
The core of each tool is a single index.html file. The tool-specific JavaScript lives in an inline <script> tag at the bottom. The CSS and JS files are shared theme assets that handle layout and navigation -- they are the same across every tool.
This structure has a few key advantages:
- Deployment is copying files. No build step, no CI pipeline. Put the files on any static host and they work.
- Each tool is self-contained. You can understand one tool without understanding any other.
- Shared navigation links all tools together. A common navbar across tools creates a cohesive feel without coupling the code.
When This Approach Breaks Down
To be fair, the zero-dependency approach is not suitable for everything. It works best for:
- Single-purpose tools with a simple input-output pattern
- Client-side-only operations (no backend required)
- Projects where long-term stability matters more than rapid feature iteration
It is a poor fit for complex interactive applications with many views, heavy state management, or real-time collaboration. If you are building a full-featured IDE or a project management app, a framework provides genuine value.
The key insight is that many useful web tools fall into the first category. A URL encoder does not need React. A hash generator does not need a build pipeline. Matching the tool to the complexity of the problem is good engineering.
The Result
The tools I have built with this approach load instantly, never break due to dependency updates, and cost essentially nothing to host on a static file server. They have been running for years with zero maintenance.
That is not a failure to adopt modern practices. It is a deliberate choice to use the simplest approach that solves the problem. Sometimes the best architecture is no architecture at all -- just the browser doing what it was designed to do.