How to minify json before storing in localstorage
- Step 1Find the heavy keys in storage — Open DevTools → Application → Local Storage (or Session Storage) for your origin. Click each key to see its value; the largest JSON blobs — cached API results, form drafts, Redux-persist state — are your minification candidates.
- Step 2Copy the stored value — Select the value for a heavy key and copy it. Paste it into the tool above, or save it as a
.jsonfile and drop it on the dropzone. Parsing runs locally in your browser. - Step 3Choose whether to drop nulls — Leave Also remove null values off to measure pure whitespace savings. Turn it on only for state blobs where a
nullfield means 'unset' and your read code treats absent the same as null — it permanently removes those fields and null array entries. - Step 4Minify and read the saving — Click Minify JSON. The strip shows
inputBytes → outputBytes · N% smaller. Remember localStorage often counts characters as ~2 bytes (UTF-16), so the real quota relief is roughly double the byte figure shown here for ASCII content. - Step 5Fix the write path, not just the value — Minifying one cached blob is a one-off. The durable fix is in your code: replace
JSON.stringify(data, null, 2)withJSON.stringify(data)everywhere you write to storage.JSON.stringifywith no indent argument already produces minified output. - Step 6If you keep hitting the quota, change storage — If a blob is large enough that minifying is a band-aid, move it to IndexedDB, which has no small fixed cap and stores structured data without a JSON string. Use Copy/Download here to grab the minified value for a one-time migration seed.
Browser storage quotas (per origin)
Approximate localStorage / sessionStorage limits. Quotas are per origin (scheme + host + port) and shared across all keys for that origin. Treat these as practical guidance, not guarantees.
| Browser | Approx. quota | Character cost | Note |
|---|---|---|---|
| Chrome / Edge | ~5 MB | ~2 bytes/char (UTF-16) | Quota shared across all keys for the origin |
| Firefox | ~10 MB | ~2 bytes/char (UTF-16) | Higher cap, same per-origin model |
| Safari | ~5 MB | ~2 bytes/char (UTF-16) | Storage can be cleared aggressively under storage pressure |
| IndexedDB (any modern browser) | Large, dynamic (often % of disk) | Structured, not a JSON string | The right home once a blob outgrows localStorage |
What minification does to a stored blob
Consequences of the JSON.parse → JSON.stringify round-trip, relevant when you re-store the value.
| Aspect | Result | Storage implication |
|---|---|---|
| Whitespace | Removed | The quota saving you are measuring (20–35% on a 2-space-indented blob) |
| Key order | Preserved | A read that depends on order is unaffected |
null fields | Kept by default; removed with the option on | Further shrink for settings/draft blobs padded with nulls |
| Numbers | Normalised (1.0 → 1) | Cosmetic; equal values on read |
| Big integers (> 2^53) | Lose precision | Store IDs as strings if you cache them |
| Invalid / truncated value | Parse error (tool refuses) | Surfaces a corrupted cache before your app reads it |
Cookbook
Real cached-state blobs, before and after. Byte counts are illustrative UTF-8 sizes; remember localStorage may count ~2 bytes per ASCII character.
Redux-persist style state cached pretty
ExampleA persisted store written with an indent argument burns quota on formatting. Removing it recovers a third of the bytes with no change to what your reducers see.
Before (pretty, 412 bytes):
{
"user": {
"id": 7,
"prefs": {
"theme": "dark",
"density": "compact"
}
}
}
After (minified, 271 bytes · 34% smaller):
{"user":{"id":7,"prefs":{"theme":"dark","density":"compact"}}}Form-draft blob padded with nulls
ExampleAuto-saved form drafts often serialize every field, most of them null. With Also remove null values on, the unset fields disappear — only enable it if your restore code treats absent and null the same.
Before:
{"title":"Draft","body":"...","tags":null,"due":null,"assignee":null}
After (removeNulls on):
{"title":"Draft","body":"..."}
Warning: restoring this draft will leave tags/due/assignee
undefined rather than null.Detect a corrupted cache before it crashes a read
ExampleWhen a write is interrupted at the quota limit, the stored string can be truncated. The tool parses first, so it tells you the blob is broken instead of your app throwing on next load.
Stored value (truncated by a quota-exceeded write):
{"items":[{"id":1},{"id":2},{"id"
Result: parse error — "Unexpected end of JSON input".
Action: clear this key and re-cache; the partial write is unusable.Cached API list deduplicated keys
ExampleA hand-merged cache that accidentally repeated a key collapses to the last value. Worth knowing if a cached field 'vanishes' after you re-minify.
Input:
{"v":1,"v":2,"data":[1,2,3]}
Minified:
{"v":2,"data":[1,2,3]}
The first v:1 was dropped — the source had a duplicate key.Settings blob with big-integer account ID
ExampleCaching a 64-bit account ID as a number loses precision. Store it as a string so the cached value round-trips exactly.
Before:
{"accountId":98765432109876543210,"theme":"dark"}
Minified:
{"accountId":98765432109876540000,"theme":"dark"}
Fix upstream:
{"accountId":"98765432109876543210","theme":"dark"}Errors and edge cases
Real errors and silent failures sourced from each platform's own documentation. Match the wording to the row, fix what the row says to fix.
Value is wrapped/escaped from a console copy
Parse errorCopying a localStorage value via console.log(JSON.stringify(localStorage.getItem(key))) wraps it in an extra layer of quotes and escapes ("{\"a\":1}"). The tool then sees a JSON string, not an object — it will parse but minify nothing useful. Copy the raw stored value (the unescaped form shown directly in the DevTools Application panel) instead.
UTF-16 character cost vs reported bytes
ExpectedThis tool reports UTF-8 bytes, but localStorage typically counts each character as ~2 bytes (UTF-16). For ASCII content your real quota relief is roughly double the byte saving shown. For multi-byte content (emoji, CJK) the relationship is closer to 1:1. Use the percentage, not the absolute bytes, as your guide to quota impact.
Truncated value from a quota-exceeded write
Parse errorWhen setItem throws QuotaExceededError mid-write some browsers leave a partial string behind. That truncated value fails to parse here ('Unexpected end of JSON input') — which is exactly what you want, since it is unusable. Clear the key and re-cache a smaller (minified) value, or move the data to IndexedDB.
Removing nulls changes restored state shape
By designAlso remove null values deletes null fields and null array elements. On restore, those properties are absent (undefined) rather than null, and array indices shift. If your hydration code distinguishes 'explicitly null' from 'never set' — common in settings and form drafts — leave the option off.
Big-integer IDs in cached records
Precision lossNumbers above 2^53 lose precision on the round-trip, so a cached 64-bit ID corrupts. This is a JavaScript number limit, not a quota concern. Cache such IDs as JSON strings so they survive both this minifier and your app's own JSON.parse/JSON.stringify cycle.
Free-tier 2 MB limit and the localStorage cap
Upgrade requiredThe free tier accepts files up to 2 MB. That is below the ~5 MB localStorage quota, but in practice if a single cached blob approaches 2 MB it is already too big for localStorage and you should move it to IndexedDB rather than minify it. Pro raises the tool's limit to 100 MB if you genuinely need to process a large export.
Date and Map values in the state
Lossy upstreamJSON.stringify already converted Date objects to ISO strings and dropped Map/Set/functions when your app first serialized the state — so by the time you paste a cached string here it is plain JSON. Minifying does not change that; just be aware the cached blob never contained the richer types in the first place.
Already-minified cache
ExpectedIf your app already writes with JSON.stringify(data) (no indent), the reported saving is ~0%. That confirms your write path is correct and the blob is simply large — minification cannot help further, and the lever is fewer fields (json-key-filter) or a move to IndexedDB.
Frequently asked questions
Does JSON.stringify() already minify?
Yes. JSON.stringify(value) with no second or third argument produces whitespace-free JSON. It is only JSON.stringify(value, null, 2) (or any indent argument) that adds formatting. If your storage writes use an indent argument, removing it minifies automatically — this tool is for measuring how much a given cached blob is wasting and for one-off cleanups.
What is the actual localStorage quota?
Roughly 5 MB per origin in Chrome, Edge and Safari, and around 10 MB in Firefox. The quota is shared across every key for that origin, and characters are usually counted as ~2 bytes each because strings are stored as UTF-16. So a 'percent smaller' here translates to roughly double that in reclaimed quota bytes for ASCII data.
Why does the saving look smaller than my quota relief?
This tool reports UTF-8 bytes; localStorage typically counts UTF-16 (~2 bytes per ASCII character). So removing 1,000 ASCII characters of whitespace shows as ~1,000 bytes here but frees ~2,000 bytes of quota. Use the percentage as the reliable comparison across both encodings.
Should I just use IndexedDB instead of minifying?
If you regularly approach the ~5 MB cap, yes — IndexedDB has a far larger, dynamic quota and stores structured data without serializing to a JSON string at all, so whitespace stops mattering. Minify localStorage blobs when they are moderately sized; migrate to IndexedDB when they are not. You can use Copy/Download here to grab the minified value as a one-time migration seed.
Will minifying corrupt cached objects with dates or class instances?
No — but those types were already flattened. JSON.stringify turns Date into an ISO string and silently drops Map, Set, and functions when your app first serialized the state. The string you paste here is plain JSON; minifying preserves it exactly (modulo whitespace and the canonicalisations). The lossy step happened upstream, not here.
Can I minify the value with code instead of pasting it?
Yes: localStorage.setItem(key, JSON.stringify(JSON.parse(value))) re-stores the parsed value in minified form. This tool does the same thing visually and, crucially, reports the byte saving and validates the value first — handy when you want to know whether the effort is worth it before changing code.
Does removing nulls help much for storage?
It depends on the blob. Settings and form-draft objects that emit every optional field as null can shrink noticeably; a dense data cache with few nulls will barely change. Try it with the option on and off and compare the two byte counts. Only keep it on if your restore logic treats absent and null the same.
Is sessionStorage different from localStorage here?
Only in lifetime — sessionStorage clears when the tab closes, localStorage persists. Both share the same per-origin quota model and the same ~2-byte-per-character cost, so everything in this guide applies identically to both.
My cached value won't parse — is it the tool?
Almost always the value. The most common causes are a console copy that double-escaped the string, or a write that was truncated when the quota was exceeded. The tool intentionally refuses to minify invalid JSON so it never hands you back a string that fails on read. Clear and re-cache the key.
Can I store the minified output directly in code?
Yes — the output is a single line with no added escaping, so you can paste it into a localStorage.setItem('key', '<output>') call or a seed/migration script. If the JSON itself contains characters that need escaping for your source language (quotes, backslashes), escape it for that language as usual; the JSON content itself is already valid.
How big a value can the tool handle?
Up to 2 MB on the free tier, 100 MB on Pro. For localStorage purposes you will almost never approach even the free limit, since a blob that large is already over the storage quota and should be in IndexedDB. The processing is in-memory in your browser.
Is my cached data uploaded anywhere?
No. Parsing and minification run entirely in your browser. The cached user data, drafts and session state you paste in never reach a JAD Apps server. Only an anonymous run counter (size and duration, no content) is recorded for signed-in users, and you can opt out in account settings.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.