Projects

JSON Converter

2024·3 min read

React
TypeScript
Tailwind CSS
Next.js
Link (opens in a new tab)GitHub (opens in a new tab)

Paste a JavaScript object or array and get it back as formatted, syntax-highlighted JSON, live as you type. Nothing is sent anywhere — the conversion happens in the tab.

On this page6 sections
  • What it handles
  • How the parsing works
  • Stack
  • Layout
  • Running locally
  • Notes

Paste a JavaScript object or array and get it back as formatted, syntax-highlighted JSON, live as you type. Nothing is sent anywhere — the conversion happens in the tab.

The reason it exists rather than any of the dozens of online JSON formatters is what it accepts. The thing on your clipboard is usually not JSON: it is a literal lifted out of a source file, with unquoted keys, single quotes and a trailing comma. JSON.parse rejects all three. So parsing tries strict JSON first — which covers most input and executes nothing — and only falls back to evaluating the text as a JavaScript expression when that fails.

That fallback is the interesting trade. It is what makes the tool useful, and it is still evaluation, so it uses Function rather than a direct eval: a function body sees only globals, where direct eval can read and write the surrounding local scope. The boundary that makes it acceptable is the deployment, not the syntax — client-side only, on text the user typed, never persisted or shared. Pointing it at input from a URL would need a real parser instead.

The renderer is memoised for a reason worth separating out: it re-runs on every keystroke and the syntax highlighter re-tokenises the entire block each time. "It re-renders" and "it is slow" are different problems, and only the second one is fixed by memo.

Next.js 14 App Router with Zustand, react-markdown and react-syntax-highlighter, on shadcn/ui.

Paste a JavaScript object or array and get it back as formatted, syntax-highlighted JSON — live, as you type. No build step in the loop, no request, nothing leaves the browser.

Live →

What it handles#

Strict JSON works, and so does the thing you actually have on your clipboard — a JavaScript literal copied out of a source file:

{ name: 'ada', ids: [1, 2, 3,], active: true }

Unquoted keys, single quotes and trailing commas all convert. Output is pretty-printed at two-space indentation with a copy button and a download button on the code block.

How the parsing works#

Strict JSON is tried first, and covers most input without executing anything:

function parseSource(source: string): unknown {
  try {
    return JSON.parse(source)
  } catch {
    return Function(`"use strict"; return (${source});`)()
  }
}

Only when JSON.parse fails does it fall back to evaluating the text as a JavaScript expression — which is what makes unquoted keys and trailing commas work at all, and the reason to paste here rather than into a plain JSON formatter.

Function rather than eval is deliberate: a direct eval can read and write the surrounding local scope, while a function body sees only globals. It is still evaluation, so the honest boundary is this — everything runs client-side on text you typed yourself, and nothing is persisted, shared, or sent anywhere. Do not extend it to parse input arriving from a URL or another user without swapping in a real parser such as JSON5 first.

Stack#

Framework Next.js 14 (App Router)
Language TypeScript
State Zustand
Rendering react-markdown + remark-gfm + remark-math
Highlighting react-syntax-highlighter
UI shadcn/ui on Radix primitives + Tailwind CSS
Input react-textarea-autosize
Themes next-themes

Layout#

app/page.tsx                          # MemorizedMarkDown + ChatPanel
components/chat/chat-panel.tsx        # docked input at the bottom
components/chat/chat-prompt-form.tsx  # the textarea
components/markdown/                  # parse, format, render, highlight
hooks/use-message.ts                  # Zustand store holding the input

The renderer is memoised on purpose. It re-runs on every keystroke and react-syntax-highlighter re-tokenises the whole block each time, so without memo typing into a large paste visibly stutters. "It re-renders" and "it is slow" are different problems, and only the second one is fixed by memoising.

Running locally#

git clone https://github.com/Vette1123/JSON-converter.git
cd JSON-converter
bun install     # or npm install
bun dev         # or npm run dev

Open http://localhost:3000. No environment variables or API keys required.

Notes#

Scaffolded from Sadge Template, which is why package.json still carries the template's name. The input dock borrows its layout from the Vercel AI chatbot example — there is no AI and no API route here.


Built by Mohamed Gado · 2024

More projects

  • NewsBite — Curated News2024 · Next.js
  • Books library2024 · React
  • Multi-purpose AI helper2023 · React
All projects
  • What it handles
  • How the parsing works
  • Stack
  • Layout
  • Running locally
  • Notes