1 slot open this monthBy the makers of shadcnregistryWe built shadcnregistry. Now we build your product.Book a callCheck availability
1 slot open this monthAI features?We build AI features into your product.Book a callCheck availability
1 slot open this month🚨 Slop police🚨 Slop police: we clean up vibe-coded apps.Report itReport your app
shadcnregistry
shadcnregistry
RegistryHealthInspect
Terms of ServicePrivacy Policy
  1. Registries
  2. pulld
  3. JSON Input

JSON Input

ui

A textarea for editing JSON by hand that says exactly where the mistake is — line, column and a caret you can jump to. Reach for it wherever a page has to let someone type a JSON value in: a setting or metadata field in an admin panel, a feature flag's payload, a webhook's test body, the request body in an API client or API playground, a job's input in a queue dashboard, a JSON/JSONB database column in an internal tool, an LLM tool-call's arguments, a connector or CI config form, a rules or mapping blob, seed data in a developer console. Common asks it answers: "json editor react", "json input component", "json textarea react", "edit json in a textarea", "react json editor shadcn", "shadcn json input", "validate json as you type", "json linter component", "show the line number of a JSON syntax error", "JSON.parse error position", "get line and column from SyntaxError", "highlight json syntax error react", "pretty print json button", "format json textarea", "tab key indent textarea react", "textarea tab inserts spaces", "jsoneditor react alternative", "react-json-editor-ajrm alternative", "codemirror json too heavy", "monaco editor just for json", "can't type a comma in my json input", "json input rewinds what I type", "controlled textarea resets while typing json", "cursor jumps to end json textarea", "react json form field". shadcn/ui has nothing for JSON: JSON.parse, JSON.stringify, SyntaxError, selectionStart, setSelectionRange and indentation each appear exactly zero times across the 223,287 bytes of component source its registry serves (62 of its 63 indexed items are actually served; questionnaire is listed but 404s on both style tracks), and its six mentions of textarea are the bare styled element itself, which knows nothing about what is typed into it. So this gets hand-written, and the hand-written version has one bug that makes the field unusable and one that makes it unhelpful. The first is the round trip. The obvious shape — parse the text, hand the object up, render JSON.stringify(object) back into the field — cannot be typed in, and not in a way a demo reveals: you have to reach a second key before it bites. The moment the text reads {"a": 1, it does not parse, there is no object to stringify, and whatever the parent hands back replaces what was being typed; even while it does parse, the round trip rewrites the text under the caret, so {"a": 1 becomes {"a":1} mid-keystroke and the comma can never be followed. Here the string is the value: `value` is text, parsing is a check that never touches it, and the parsed value is handed over as metadata through onParse. Keep the string in your own state — const [text, setText] = useState(…), not JSON.stringify(config) — and nothing fights the caret. The second is the error location, and the browser will not give it to you. No engine puts a position property on the SyntaxError that JSON.parse throws — the error carries message and stack and nothing else. The message is not an interface either: V8 writes "Expected ',' or ']' after array element in JSON at position 5 (line 1 column 6)" for some inputs and "Unexpected token ']', \"[1,2,]\" is not valid JSON" for others, with no position at all in the second form; Firefox writes "at line 1 column 6 of the JSON data"; JavaScriptCore writes "JSON Parse error: Expected ']'" and never includes a position. A component that scrapes the message therefore gets no location on Safari and no location on Chrome either for a whole class of mistakes — the trailing comma among them, which is the single most common thing wrong with hand-edited JSON. So this walks the RFC 8259 grammar itself, in one pass with an explicit stack rather than recursion so a deeply nested paste cannot overflow it, and reports the offset of the first offending character with a message written once and read the same way in every browser. JSON.parse stays the authority on whether the text is valid and the scanner is only ever asked where a failure is, so it can never reject a document the runtime would have accepted. Nothing is validated mid-keystroke: half-typed JSON is always broken, and a field that turns red on the second character is red the whole time it is in use, so the verdict waits for a pause (debounceMs, 200ms) and shows nothing in the meantime rather than a stale answer — which is also what keeps a few hundred kilobytes of payload from being re-parsed on every keypress. Formatting only ever happens when asked, through the Format button or formatOnBlur, never while typing, because reformatting replaces the whole value and throws the caret to the end of the document. Tab indents and Shift+Tab outdents every line the selection touches — and a selection that ends at the next line's first column stops short of it, so the closing brace you dragged past is not pushed over. A Tab that is always captured is a keyboard trap, so Escape releases it: Escape then Tab moves focus on, as WCAG 2.1.2 requires, and the release is announced in the field's own sr-only description instead of being left to be discovered. Escape is not swallowed, so it still reaches the dialog the editor sits in. The caret is restored after every indent through a layout effect rather than inside the key handler, because the value React commits replaces the whole contents of the textarea and a browser parks the caret at the end of a textarea whose value was replaced. Accessibility and form wiring: aria-invalid once the verdict is in and not before, the status line is aria-live="polite" and only ever updated after the pause, a caller's own aria-describedby is kept alongside the status and the hint rather than overwritten, the error message carries "Line 3, column 1" as text rather than relying on colour, and name submits the text like any textarea. autoCapitalize, autoCorrect, autoComplete and spellCheck default off, because a mobile keyboard rewrites a key name between the keypress and the change event and spellcheck underlines every identifier in the document; all four are overridable. parseJsonInput, scanJson and formatJson are exported as pure functions for the places the same answer is wanted outside React — a route handler checking what arrived, a CLI validating a config file, a test asserting on a fixture. Styled with shadcn tokens (input, ring, destructive, muted-foreground) for light and dark; className merges rather than fights; one file, zero dependencies, no Radix, no editor library, no JSON parser of its own. Distinct from json-viewer, which renders a parsed value as a read-only collapsible tree: that one is for showing a payload the reader did not write, this one is for changing it. Pair them when a panel both displays and edits. Distinct from code-block, which shows JSON as static text to copy, and from autosize-textarea, which grows to its content but knows nothing about what is in it. Distinct from official shadcn/ui's textarea, which is the plain field this is built on and nothing more: it accepts any string, has no idea whether it holds JSON, and reports no error, no line and no column — so validating what someone typed into it, finding where the mistake is, and getting a parsed value back out are all still yours to write.

pnpm dlx shadcn@latest add "https://pulld.pages.dev/r/json-input.json"
Premium blocks for shadcn/uiShadcnblocks.com · AffiliateExplore
components/ui/json-input.tsx