partial-json
partial-json parses JSON strings that are cut off mid-stream. When an LLM streams a JSON response token by token, JSON.parse fails on every chunk until the very last one; partial-json's parse function instead returns the best complete value it can build from what has arrived so far, so you can render structured data live while it streams. An Allow bitmask controls which types may be incomplete: with STR a half-finished string is returned as-is, without it the parser waits for the closing quote. It is a small pure-JavaScript library with zero dependencies and both CJS and ESM builds.
The standard small tool for one specific job: showing structured LLM output while it streams, which explains 5M+ weekly downloads on 243 stars. Use it for that; for repairing invalid JSON or parsing huge streams pick a different tool.
Use it if
- You stream JSON from an LLM (tool arguments, structured output) and want to update the UI on every chunk instead of waiting for the final token
- You want JSON.parse-compatible behavior on complete input with graceful handling of truncated input, in one function
- You need control over what may be partial, for example allow half-open objects but suppress half-finished string values so users never see cut-off text
- You need to salvage a value from a response that hit a max-token limit and ended mid-object
- Your JSON is malformed rather than truncated: partial-json throws on actually invalid input like single quotes or trailing commas, which is jsonrepair territory
- You use the Vercel AI SDK or another framework whose streamObject/structured-output helpers already do incremental parsing internally, in which case adding this is redundant
- You process multi-megabyte streams where you need incremental event-based parsing: this library reparses the full accumulated string on every call, which is O(n) per chunk and O(n^2) over a stream
- You want a battle-hardened core dependency: this is a 0.x package from a small maintainer with sparse commit activity, fine as a UI nicety, riskier as a load-bearing parser
Setup reality
npm i partial-json and you are done: zero dependencies, TypeScript types included, works in Node and browsers with both require and import. The things to internalize are behavioral, not install-related. parse defaults to Allow.ALL, which will happily return half-written strings and numbers, so decide your bitmask deliberately. You also still own the streaming loop: accumulate chunks into one string yourself and call parse on each update, and wrap calls in try/catch because a chunk boundary can still leave text the parser considers malformed rather than merely incomplete.
Patterns
Parse complete JSON like JSON.parseparse-complete-json
import { parse } from "partial-json";
const result = parse('{"key": "value"}');
console.log(result); // { key: 'value' }On complete input parse behaves like JSON.parse, so you can use one code path for both mid-stream and final payloads.
Parse a truncated string with defaultsparse-truncated-json
import { parse } from "partial-json";
const result = parse('[{"key1": "value1", "key2": ["value2');
console.log(result);
// [ { key1: 'value1', key2: [ 'value2' ] } ]The default allowance is Allow.ALL, so everything including half-finished strings is returned; pass a bitmask if that is too eager.
Choose which types may be incompleterestrict-partial-types
import { parse, Allow } from "partial-json";
parse('{"key": "v', Allow.STR | Allow.OBJ);
// { key: 'v' }
parse('{"key": "v', Allow.OBJ);
// {} (string not closed, so the pair is withheld)Types not in the mask only appear once the parser is sure they are complete; note the unclosed object itself needs OBJ in the mask.
Render streamed LLM JSON incrementallystream-llm-output
import { parse, Allow } from "partial-json";
let buffer = "";
for await (const chunk of llmStream) {
buffer += chunk;
try {
render(parse(buffer, Allow.OBJ | Allow.ARR | Allow.STR));
} catch {
// chunk boundary left unparseable text; wait for more
}
}You accumulate the full string and reparse each time; that is fine for typical LLM response sizes but is quadratic over very long streams.
Suppress half-finished string values in a UIhide-partial-strings
import { parse, Allow } from "partial-json";
const safe = parse(buffer, Allow.OBJ | Allow.ARR);
// string fields only appear after their closing quoteUseful when showing a cut-off URL or sentence would confuse users; keys and completed values still stream in as they finish.
Import allowance flags without the Allow objectimport-flags-directly
import { parse, STR, OBJ, ARR } from "partial-json";
const result = parse(buffer, STR | OBJ | ARR);Each Allow property is also a named export; the values are plain numbers combined with bitwise OR.
Handle NaN and Infinity literalsparse-special-values
import { parse } from "partial-json";
parse("-Inf"); // -Infinity
parse("NaN"); // NaNThese are not valid JSON, but Python's json.dumps emits them, so the parser accepts them; gate with Allow.SPECIAL if you do not want partial forms.
Distinguish truncated from malformed inputcatch-malformed-json
import { parse } from "partial-json";
try {
parse("wrong");
} catch (e) {
console.log(e.name); // MalformedJSON
}Truncated JSON returns a value; genuinely invalid JSON throws MalformedJSON. Do not use this library expecting repair of bad syntax.
Allow partial literals like tru and nulparse-partial-atoms
import { parse, Allow } from "partial-json";
parse('[tru', Allow.ARR | Allow.BOOL); // [true]
parse('[nul', Allow.ARR | Allow.NULL); // [null]With the matching atom flag the parser commits to the only literal the prefix can become; without it the element is omitted until complete.
Use the convenience flag groupsgroup-flags
import { parse, Allow } from "partial-json";
Allow.COLLECTION; // ARR | OBJ
Allow.ATOM; // strings, numbers, booleans, null, specials
Allow.ALL; // everything (the default)
parse(buffer, Allow.COLLECTION);Allow.COLLECTION is the common choice for UIs: structure streams in live while every scalar value waits until it is complete.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| jsonrepair | npm | Your input is broken JSON (quotes, commas, comments), not just truncated JSON, and you want it fixed into valid output. |
| best-effort-json-parser | npm | You want maximally forgiving parsing of messy partial JSON without configuring what may be incomplete. |
| @streamparser/json | npm | You need true incremental SAX-style parsing of large JSON streams without reparsing from the start. |