every time i needed to format some json, i'd google "json formatter", click the first result, paste my data, and grab the formatted output. did this for years.
then one day i was debugging an API response that had user emails, auth tokens, and internal IDs in it. pasted the whole thing into some random website without thinking.
that was the moment i realized how dumb that was.
the problem with online formatters
when you paste data into a web-based tool, you have no idea what happens to it. some of these sites:
- log everything you paste to their servers
- run analytics on your input data
- store it temporarily (or permanently) for "improving their service"
- some are literally just data collection fronts
even the "good" ones that say they process client-side — how do you verify that? you'd need to audit their js bundle, check for beacon calls, inspect network requests. nobody does that.
for formatting json? you don't need a server. it's a completely client-side operation. JSON.parse() and JSON.stringify() handle 99% of it.
building a local json formatter
it's genuinely like 20 lines of code:
<!DOCTYPE html>
<html>
<body>
<textarea id="input" placeholder="paste json here" rows="15" cols="80"></textarea>
<br>
<button onclick="formatJSON()">format</button>
<button onclick="minifyJSON()">minify</button>
<pre id="output"></pre>
<script>
function formatJSON() {
try {
const obj = JSON.parse(document.getElementById('input').value);
document.getElementById('output').textContent = JSON.stringify(obj, null, 2);
} catch (e) {
document.getElementById('output').textContent = 'invalid json: ' + e.message;
}
}
function minifyJSON() {
try {
const obj = JSON.parse(document.getElementById('input').value);
document.getElementById('output').textContent = JSON.stringify(obj);
} catch (e) {
document.getElementById('output').textContent = 'invalid json: ' + e.message;
}
}
</script>
</body>
</html>
save that as an html file, open it in your browser. done. no server, no tracking, no data leaving your machine.
making it actually useful
the basic version works but i wanted more. here's what i ended up adding:
syntax highlighting
you can do this with a simple regex replacer:
function syntaxHighlight(json) {
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return json.replace(
/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
function (match) {
let cls = 'number';
if (/^\"/.test(match)) {
cls = /:$/.test(match) ? 'key' : 'string';
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
}
);
}
path copying
when you're debugging deeply nested json, it's useful to click on a key and get its full path (like data.users[0].email). this is a bit more involved but super helpful:
function buildTree(obj, path = '') {
if (typeof obj !== 'object' || obj === null) return;
for (const key of Object.keys(obj)) {
const currentPath = Array.isArray(obj)
? path + '[' + key + ']'
: (path ? path + '.' + key : key);
console.log(currentPath, '=', obj[key]);
buildTree(obj[key], currentPath);
}
}
diff comparison
paste two json objects and see what's different. useful when you're comparing API responses:
function jsonDiff(obj1, obj2, path = '') {
const diffs = [];
const allKeys = new Set([...Object.keys(obj1 || {}), ...Object.keys(obj2 || {})]);
for (const key of allKeys) {
const fullPath = path ? path + '.' + key : key;
if (!(key in obj1)) {
diffs.push({ path: fullPath, type: 'added', value: obj2[key] });
} else if (!(key in obj2)) {
diffs.push({ path: fullPath, type: 'removed', value: obj1[key] });
} else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
diffs.push(...jsonDiff(obj1[key], obj2[key], fullPath));
} else if (obj1[key] !== obj2[key]) {
diffs.push({ path: fullPath, type: 'changed', from: obj1[key], to: obj2[key] });
}
}
return diffs;
}
other tools i stopped outsourcing
once i went down this rabbit hole, i realized i was sending data to random servers for a bunch of stuff:
-
base64 encoding/decoding —
btoa()andatob()exist in every browser -
url encoding —
encodeURIComponent()is right there - hash generation — the Web Crypto API handles SHA-256 natively
- regex testing — you can test regex in the browser console
- jwt decoding — it's literally just base64, split on dots
// decode a JWT in one line
const payload = JSON.parse(atob(token.split('.')[1]));
all of these are trivial to do locally. no server needed.
the bigger principle
there's a pattern here. developers (myself included) default to googling for an online tool when the functionality is already built into the platform we're using. we send sensitive data to third parties for operations that should never leave our machine.
i'm not saying every online tool is malicious. most aren't. but when the operation is purely computational and doesn't need a server, why take the risk?
especially when you're dealing with:
- API keys and tokens
- user data from production
- internal configuration
- anything with PII
what i ended up building
i got tired of maintaining a folder of random html files, so i put together a collection of browser-based dev tools that all run client-side. json formatter, base64 encoder, hash generator, regex tester, jwt decoder, and a bunch more.
the whole point is: your data stays in your browser. nothing gets sent anywhere. you can verify this by opening the network tab — zero outbound requests.
i put it up at devtools-site-delta.vercel.app if you want to use it. there are about 22 tools on there now. all free, all client-side.
or just build your own. seriously, most of these tools are under 50 lines of javascript. the code examples above are basically all you need.
tl;dr
- stop pasting sensitive data into random websites
- json formatting, base64, hashing, regex testing — all doable in the browser natively
- if you need a tool, build it locally or use one that's verifiably client-side
- it takes less time than you think
your data isn't worth the convenience.
Top comments (0)