Skip to tool
FeuTex · free tools runs in-browser no bloat built by LiMiT

Schema Formatter

Clean up JSON-LD schema markup fast: pretty print, minify, sort keys, and optionally wrap it in a <script type="application/ld+json"> tag. You can paste raw JSON-LD or an HTML snippet containing JSON-LD script tags—everything runs locally in your browser.

Category: SEO · URL: /tools/schema-formatter.html

Tip: Press Ctrl/⌘ + Enter to format. If you paste HTML with multiple JSON-LD scripts, the tool outputs a JSON array.

No network calls. Your schema stays in your browser.

Privacy: runs locally in your browser. No uploads, no tracking scripts.

How to use

Use this tool to format or compress your schema markup without sending data anywhere.

  1. Paste your JSON-LD (or HTML containing application/ld+json script tags) into the input.
  2. Choose indentation, optionally enable key sorting, and choose whether to wrap output in a script tag.
  3. Click Format for readable output or Minify for compact output. Use Validate to check JSON syntax.
Keywords this page targets (natural cluster): schema formatter, json-ld formatter, schema markup formatter, pretty print json-ld, minify json-ld, format structured data, json-ld beautifier, schema json formatter, ld+json formatter, format schema.org json, wrap json-ld script tag, extract json-ld from html, sort json keys online, offline json-ld formatter, schema markup minifier, validate json-ld offline, format json for rich results, beautify schema markup
Secondary intents covered: Pretty-format JSON-LD for readability before publishing, Minify JSON-LD to reduce page weight, Extract JSON-LD from HTML <script> blocks, Sort object keys for stable diffs and reviews, Wrap formatted JSON-LD back into an application/ld+json script tag, Validate JSON syntax and locate parsing errors, Copy formatted schema to clipboard, Download JSON-LD as a .jsonld file, Standardize indentation across multiple schema snippets

FAQ

Can I paste HTML instead of raw JSON-LD?

Yes. If your input contains <script type="application/ld+json">, the tool extracts and formats the JSON-LD inside.

What happens if the HTML contains multiple JSON-LD script tags?

The tool parses each script and outputs a single JSON array containing each JSON-LD block.

Does “Sort keys (deep)” change my schema meaning?

No. JSON object key order doesn’t affect meaning, but sorting can make reviews and diffs easier; array order is kept.

Will this validate my schema against Google rich result requirements?

No. It validates JSON syntax only; use a schema checker for structured-data rules.

Why does it say “Invalid JSON” even though my code looks right?

Common causes are trailing commas, smart quotes, or pasted JavaScript objects that aren’t strict JSON (unquoted keys, single quotes).

Can I output a ready-to-paste script tag?

Yes. Enable Wrap in <script> to generate a complete application/ld+json block.

Is my schema sent to a server?

No. The formatter runs entirely in your browser and does not make network requests.

","expected":"{\n \"@type\": \"Thing\",\n \"name\": \"X\",\n \"@context\": \"https://schema.org\"\n}"},{"name":"Deep sort keys","input":"{\"b\":1,\"a\":2,\"c\":{\"d\":4,\"b\":3}}","expected":"{\n \"a\": 2,\n \"b\": 1,\n \"c\": {\n \"b\": 3,\n \"d\": 4\n }\n}"}]; // The tool JS must attach: // - window.feutexReset(): resets UI // - window.feutexSelfTest(tests): returns string log (PASS/FAIL) // - window.feutexInit(): optional (function(){ const log = document.getElementById('selfTestLog'); const btn = document.getElementById('selfTestBtn'); const resetBtn = document.getElementById('resetBtn'); function setLog(txt){ if(log) log.textContent = txt || ''; } btn?.addEventListener('click', async () => { try { if(typeof window.feutexSelfTest !== 'function') { setLog('Self-test missing: window.feutexSelfTest not defined.'); return; } const out = await window.feutexSelfTest(FEUTEX_SELF_TESTS); setLog(out || 'No self-test output.'); } catch(e) { setLog('Self-test error: ' + (e?.message || e)); } }); resetBtn?.addEventListener('click', () => { try { if(typeof window.feutexReset === 'function') window.feutexReset(); setLog(''); } catch(e) { setLog('Reset error: ' + (e?.message || e)); } }); try { if(typeof window.feutexInit === 'function') window.feutexInit(); } catch(_e) {} document.getElementById('yr').textContent = String(new Date().getFullYear()); })(); `; } return json; } function doTransform(pretty){ setError(''); const inputEl = $('toolInput'); const outEl = $('toolOutput'); if(!inputEl || !outEl) return; try{ const parsed = parseMaybeHtml(inputEl.value); outEl.value = formatObj(parsed.obj, pretty, getIndentValue(), shouldSort(), shouldWrap()); }catch(err){ outEl.value = ''; setError(err && err.message ? err.message : String(err)); } } function validateOnly(){ setError(''); const inputEl = $('toolInput'); const outEl = $('toolOutput'); if(!inputEl || !outEl) return; try{ const parsed = parseMaybeHtml(inputEl.value); const t = parsed.obj; const typeInfo = (t && typeof t === 'object') ? (Array.isArray(t) ? `Array (${t.length})` : 'Object') : typeof t; outEl.value = `Valid JSON. Parsed type: ${typeInfo}.`; }catch(err){ outEl.value = ''; setError(err && err.message ? err.message : String(err)); } } function fallbackCopy(text){ const ta = document.createElement('textarea'); ta.value = text; ta.setAttribute('readonly', ''); ta.style.position = 'fixed'; ta.style.left = '-9999px'; document.body.appendChild(ta); ta.select(); try{ document.execCommand('copy'); }catch(_e){} document.body.removeChild(ta); } function copyOutput(){ const outEl = $('toolOutput'); if(!outEl) return; const text = outEl.value || ''; if(!text){ setError('Nothing to copy.'); return; } setError(''); if(navigator.clipboard && navigator.clipboard.writeText){ navigator.clipboard.writeText(text).catch(()=>fallbackCopy(text)); }else{ fallbackCopy(text); } } function downloadOutput(){ const outEl = $('toolOutput'); if(!outEl) return; const text = outEl.value || ''; if(!text){ setError('Nothing to download.'); return; } setError(''); const blob = new Blob([text], { type: 'application/json;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'schema.jsonld'; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url), 1000); } window.feutexReset = function(){ const inputEl = $('toolInput'); const outEl = $('toolOutput'); if(inputEl) inputEl.value = ''; if(outEl) outEl.value = ''; const indentSel = $('indentSize'); if(indentSel) indentSel.value = '2'; const sortEl = $('sortKeys'); if(sortEl) sortEl.checked = false; const wrapEl = $('wrapScript'); if(wrapEl) wrapEl.checked = false; setError(''); }; window.feutexInit = function(){ const bind = (id, fn) => { const el = $(id); if(el) el.addEventListener('click', fn); }; bind('btnFormat', () => doTransform(true)); bind('btnMinify', () => doTransform(false)); bind('btnValidate', validateOnly); bind('btnCopy', copyOutput); bind('btnDownload', downloadOutput); bind('btnReset', window.feutexReset); const inputEl = $('toolInput'); if(inputEl){ inputEl.addEventListener('keydown', (e) => { if((e.ctrlKey || e.metaKey) && e.key === 'Enter'){ e.preventDefault(); doTransform(true); } }); } }; function runTransform(input, opts){ const extracted = extractLdJsonFromHtml(input); const jsonText = extracted !== null ? extracted : (input || '').trim(); const obj = JSON.parse(jsonText); return formatObj(obj, opts.pretty, opts.indent, opts.sort, opts.wrap); } window.feutexSelfTest = function(){ const tests = [ { name: 'Pretty basic', input: '{"@context":"https://schema.org","@type":"Person","name":"Ada"}', expected: '{\n "@context": "https://schema.org",\n "@type": "Person",\n "name": "Ada"\n}', opts: { pretty: true, indent: 2, sort: false, wrap: false } }, { name: 'Minify basic', input: '{"@context":"https://schema.org","@type":"Person","name":"Ada"}', expected: '{"@context":"https://schema.org","@type":"Person","name":"Ada"}', opts: { pretty: false, indent: 2, sort: false, wrap: false } }, { name: 'Extract JSON-LD from script', input: '', expected: '{\n "@type": "Thing",\n "name": "X",\n "@context": "https://schema.org"\n}', opts: { pretty: true, indent: 2, sort: false, wrap: false } }, { name: 'Deep sort keys', input: '{"b":1,"a":2,"c":{"d":4,"b":3}}', expected: '{\n "a": 2,\n "b": 1,\n "c": {\n "b": 3,\n "d": 4\n }\n}', opts: { pretty: true, indent: 2, sort: true, wrap: false } } ]; let pass = 0; const lines = []; for(const t of tests){ try{ const out = runTransform(t.input, t.opts); const ok = out === t.expected; lines.push(`${ok ? 'PASS' : 'FAIL'}: ${t.name}${ok ? '' : `\nExpected:\n${t.expected}\nGot:\n${out}`}`); if(ok) pass++; }catch(e){ lines.push(`FAIL: ${t.name}\nError: ${(e && e.message) ? e.message : String(e)}`); } } lines.unshift(`Schema Formatter self-test: ${pass}/${tests.length} passed`); return lines.join('\n\n'); }; if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', () => { if(window.feutexInit) window.feutexInit(); }); }else{ if(window.feutexInit) window.feutexInit(); } })();