How to remove null fields from json stored in localstorage
- Step 1Dump the stored value to a file — In DevTools, read the key and save it:
copy(localStorage.getItem('userPrefs'))then paste into a.jsonfile, or use the Application panel to copy the value. The file must be a single valid JSON document. - Step 2Drop the file into the tool — Drag the saved file onto the dropzone. The header shows its current size — note that number; it's your baseline for measuring the saving.
- Step 3Choose what to strip —
null valuesis on by default. For settings blobs, also tickempty strings(cleared text fields) andempty objects. Tickempty arraysonly if an empty list (e.g. 'no pinned items') isn't itself a meaningful preference. - Step 4Keep Deep on for nested preference trees — Leave Deep checked so nested config like
{"editor":{"font":null}}is cleaned at every level, not just the top object. - Step 5Run and compare sizes — Click Strip Values. Compare the output size against the baseline from step 2 to quantify the quota saving. The 'N values stripped' counter shows how many dead fields were removed.
- Step 6Wire the rule into your storage helper — Replicate it before
setItem:const clean = (o) => Object.fromEntries(Object.entries(o).filter(([,v]) => v != null)); localStorage.setItem('userPrefs', JSON.stringify(clean(prefs)));. Run it on read too, to scrub legacy stored data.
What to strip from a client-storage blob
Map each target to the kind of dead field it removes from persisted UI state.
| Target | Typical client-storage field | Strip it? | Default |
|---|---|---|---|
null values | "lastRoute": null never-set preference | Yes — pure quota waste | On |
empty strings | "draft": "" a cleared text input | Usually yes for drafts | Off |
empty arrays | "pinned": [] no pinned items | Only if [] isn't a real 'none' state | Off |
empty objects | "filters": {} no active filters | Usually yes | Off |
undefined values | n/a — JSON files have no undefined | No (no-op for files) | Off |
Preserved preference values
Boolean and zero preferences must survive a clean — and they do.
| Stored value | Meaning | Result |
|---|---|---|
"darkMode": false | User chose light mode | Preserved |
"badgeCount": 0 | Zero unread items | Preserved |
"sidebar": null | Never set | Stripped (if null ticked) |
"note": " " | A space — not empty | Preserved |
"tabs": [] | No open tabs | Stripped only if empty arrays ticked |
Tool limits vs localStorage budget
The tool's file limits (lib/tier-limits.ts) far exceed the browser's ~5 MB per-origin localStorage cap, so size is rarely the constraint here.
| Limit | Value |
|---|---|
| localStorage per origin (typical) | ~5 MB |
| Tool free file limit | 2 MB |
| Tool Pro file limit | 100 MB |
| Accepted extensions | .json .ndjson .jsonl .txt |
| Upload? | No — in-browser only |
Cookbook
Before/after on synthetic localStorage blobs. Sizes are illustrative; measure your own.
Trim a settings blob
ExampleA preferences object with several never-set keys. Default strip (null, Deep on) leaves only the active settings.
Input (userPrefs.json):
{
"theme": "dark",
"sidebar": null,
"lastRoute": null,
"fontSize": 14
}
Strip Values (targets: null · Deep on):
{
"theme": "dark",
"fontSize": 14
}
2 values stripped.Keep a 'light mode' boolean
ExampledarkMode:false is a real choice. Stripping null removes the dead key but preserves the false.
Input:
{ "darkMode": false, "accent": null, "badgeCount": 0 }
Strip Values (targets: null · Deep on):
{ "darkMode": false, "badgeCount": 0 }
false and 0 survive; null accent is removed.Clean a nested editor config
ExampleDeep mode reaches into nested preference objects; tick empty objects to collapse a config branch that ends up empty.
Input:
{
"editor": { "font": null, "wrap": null }
}
Strip Values (targets: null + empty objects · Deep on):
{}
3 values stripped (font, wrap, then empty editor). The whole blob collapses.Full squeeze for quota pressure
ExampleWhen close to the 5 MB cap, strip all four empties to recover the most space from a draft-heavy blob.
Input:
{
"draft": "",
"pinned": [],
"filters": {},
"name": "My doc"
}
Strip Values (targets: null + empty strings + empty arrays + empty objects · Deep on):
{ "name": "My doc" }Scrub legacy data on app load
ExampleApply the same rule when reading, so old blobs with accumulated nulls get cleaned the next time the app boots.
const raw = JSON.parse(localStorage.getItem('prefs') || '{}');
const clean = (o) => Object.fromEntries(
Object.entries(o).filter(([, v]) => v != null)
);
localStorage.setItem('prefs', JSON.stringify(clean(raw)));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.
null means 'cleared by user'
CautionIf null is a deliberate signal (the user explicitly cleared a value) rather than 'never set', stripping it loses that meaning. Store a sentinel like false or 0 for 'cleared', and reserve null for 'unset' — or don't tick null for that blob.
false / 0 preference values
Preservedfalse (e.g. darkMode off) and 0 (e.g. zero badge count) are real preferences and are never stripped. There's no truthiness mode that could remove them.
Empty array as a real 'none' state
Caution"pinned": [] may legitimately mean 'no pinned items', distinct from never having pinned. Leave empty arrays unticked when the empty list is itself part of the state.
Whole blob collapses to {}
ExpectedWith null + empty objects and Deep on, a nested config whose every field is null collapses to {} and then strips, leaving {}. That's correct — but confirm you didn't expect any of those fields to survive.
Stored value is a JSON string, double-encoded
Parse errorIf you stored JSON.stringify(JSON.stringify(obj)), the file is a quoted string, not an object — stripping does nothing useful (a string has no keys). Decode one level before dropping it in.
Invalid JSON copied from DevTools
Parse errorA truncated copy or a value that was never valid JSON throws on parse. Verify the copied text is complete; repair with json-format-fixer if needed.
Whitespace string in a draft field
PreservedA draft of " " is not an empty string (length 1), so it's kept. Trim before stripping if a whitespace-only draft should be discarded.
File larger than the tier cap
BlockedFree caps at 2 MB. A localStorage blob is almost always well under that, but an exported multi-key store could exceed it — upgrade to Pro (100 MB) or split keys.
Output not minified for storage
ExpectedOutput is 2-space pretty JSON. Since localStorage stores a string, minify the cleaned result with json-minifier to save the extra whitespace bytes before setItem.
Frequently asked questions
How much localStorage space can stripping save?
It depends on how many keys are null. Settings blobs where optional preferences are mostly unset can be a third or more dead keys. Note the file size in the header before and after stripping to measure your exact saving, then minify for the final stored string.
Will it remove a darkMode:false or a zero count?
No. false and 0 are real preference values and are always preserved. Only null (and any empty-string/array/object you tick) is removed.
What if null means the user cleared a value?
Then don't strip it — stripping erases that signal. Use a sentinel such as false, 0, or "" to mean 'cleared' explicitly, and keep null strictly for 'never set' so it's safe to strip.
Is my localStorage content uploaded?
No. The file is read and stripped entirely in your browser. localStorage content, including user preferences, never reaches JAD Apps servers.
Can I paste the value instead of saving a file?
No — the tool is file-only. Save the localStorage value to a .json file (e.g. paste a copy(localStorage.getItem(...)) result) and drop it.
Should I run stripping on read or on write?
Both is ideal. On write, strip before setItem to keep new data lean. On read, apply the same function to scrub legacy blobs that accumulated nulls before you added the rule.
Will the output be ready to store directly?
It's pretty-printed JSON, which wastes a few whitespace bytes for a string store. Run it through json-minifier first to store the most compact version.
Why does my whole blob become {} after stripping?
With null plus empty objects and Deep on, a config whose every field is null collapses fully. If you expected some fields to remain, check whether they were actually null in the source.
Does it help with QuotaExceededError?
Indirectly — smaller stored strings use less quota, reducing how often a setItem exceeds the ~5 MB per-origin budget. Pair stripping with minification for the biggest reduction.
What about sessionStorage or IndexedDB?
The tool doesn't touch browser storage directly — it cleans a JSON file you export. The same cleaned shape applies whether you persist it to localStorage, sessionStorage, or IndexedDB.
Does the undefined option do anything here?
No. A JSON file can't contain undefined, so the checkbox is a no-op on dropped files. In your app code, filter with v != null to catch both null and undefined before storing.
Is there a size limit on the file?
Yes — 2 MB on Free, 100 MB on Pro. A typical localStorage blob is far smaller, so this is rarely a constraint for client-storage cleanup.
Privacy first
Conversion runs locally in your browser. No file is uploaded — only metadata counters are saved for signed-in dashboard stats.