{"_id":"@axonpack/react-pretty-print","name":"@axonpack/react-pretty-print","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@axonpack/react-pretty-print","version":"0.1.0","description":"Interactive JSON tree for React and React Native — you supply the View and Text primitives","author":{"name":"Md Asadujjaman","email":"abappi2019@gmail.com","url":"https://github.com/abappi19"},"keywords":["json","pretty-print","json-tree","react","react-native"],"bugs":{"url":"https://github.com/axonpack/axonpack/issues"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/axonpack/axonpack.git","directory":"packages/@axonpack/react-pretty-print"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"main":"build/index.js","types":"build/index.d.ts","sideEffects":false,"exports":{".":{"types":{"expo-source":"./src/index.ts","default":"./build/index.d.ts"},"expo-source":"./src/index.ts","default":"./build/index.js"},"./themes":{"types":{"expo-source":"./src/themes/index.ts","default":"./build/themes/index.d.ts"},"expo-source":"./src/themes/index.ts","default":"./build/themes/index.js"},"./package.json":"./package.json"},"scripts":{"build":"rm -rf build && tsc","clean":"rm -rf build","lint":"oxlint src","format":"oxlint src --fix","test":"bun test src","check-types":"tsc --noEmit -p tsconfig.check.json"},"devDependencies":{"@types/bun":"^1.4.2","@types/react":"~19.2.2","@types/react-dom":"^19.2.0","linter":"*","oxlint":"^1.80.0","prettier":"^3.0.0","react":"19.2.3","react-dom":"19.2.3","typescript":"^5.9.2"},"peerDependencies":{"react":">=18"},"_id":"@axonpack/react-pretty-print@0.1.0","homepage":"https://github.com/axonpack/axonpack#readme","_nodeVersion":"24.11.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-ied9ipagmfNK9/n4EQW6folAtsElDPdgHuECB+zWos++dVCSWEDAch9Wkooc9ZFiK/6OVWSTt0g8Y4i+JMKumA==","shasum":"596e1f6dfef92566d4d601a0f616d21f9f31b4c2","tarball":"https://registry.npmjs.org/@axonpack/react-pretty-print/-/react-pretty-print-0.1.0.tgz","fileCount":122,"unpackedSize":456231,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIAPjguwgUHNmC2WdA/hWuhjRTDI8i4ikqvntFsjRvNg7AiA8Eo6yWbUFagzhZPJnnfmoei4jWEfkkb0aCUqczCPs/A=="}]},"_npmUser":{"name":"abappi19","email":"abappi2019@gmail.com"},"directories":{},"maintainers":[{"name":"abappi19","email":"abappi2019@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/react-pretty-print_0.1.0_1789029062501_0.254196964702593"},"_hasShrinkwrap":false}},"time":{"created":"2026-09-10T08:31:02.267Z","0.1.0":"2026-09-10T08:31:02.693Z","modified":"2026-09-10T08:31:03.274Z"},"maintainers":[{"name":"abappi19","email":"abappi2019@gmail.com"}],"description":"Interactive JSON tree for React and React Native — you supply the View and Text primitives","homepage":"https://github.com/axonpack/axonpack#readme","keywords":["json","pretty-print","json-tree","react","react-native"],"repository":{"type":"git","url":"git+https://github.com/axonpack/axonpack.git","directory":"packages/@axonpack/react-pretty-print"},"author":{"name":"Md Asadujjaman","email":"abappi2019@gmail.com","url":"https://github.com/abappi19"},"bugs":{"url":"https://github.com/axonpack/axonpack/issues"},"license":"MIT","readme":"# @axonpack/react-pretty-print\n\nCollapsible JSON and XML trees and a syntax highlighter for **React and React Native from one\nimplementation**. You pass the container, text and pressable components in; the package owns the\nlogic — expansion state, array chunking, collapsed-node previews, an XML parser, a 38-language\ntokenizer and 130 palettes.\n\nNo dependencies. Nothing in it imports `react-native` or touches the DOM, so a web project installs\nnothing but React and configures no bundler alias, and a React Native project passes its own\ncomponents straight in with no adapter to write.\n\n## Getting started\n\n```sh\nnpm install @axonpack/react-pretty-print\n# or: bun add @axonpack/react-pretty-print\n```\n\nReact 18 or newer is a peer dependency. There is no native module, so no `pod install` and no\nprebuild — it runs in Expo Go.\n\n## Usage\n\nOn the web, `domPrimitives` ships with the package:\n\n```tsx\nimport { CodeHighlight, JsonTree, XmlTree, domPrimitives } from '@axonpack/react-pretty-print';\nimport { LIGHT_THEME } from '@axonpack/react-pretty-print/themes';\n\nconst Example = () => (\n  <>\n    <JsonTree primitives={domPrimitives} value={response} theme={LIGHT_THEME} />\n    <XmlTree primitives={domPrimitives} source={feed} theme={LIGHT_THEME} />\n    <CodeHighlight\n      primitives={domPrimitives}\n      code={src}\n      language=\"typescript\"\n      theme={LIGHT_THEME}\n    />\n  </>\n);\n```\n\nOn React Native, react-native's own components satisfy the contract as-is:\n\n```tsx\nimport { Pressable, Text, View } from 'react-native';\nimport { JsonTree } from '@axonpack/react-pretty-print';\n\nconst Example = () => <JsonTree primitives={{ View, Text, Pressable }} value={response} />;\n```\n\nSee `example-web/` and `example-native/` for both, including the context menu, a theme picker and a\nlanguage picker.\n\n---\n\n## `<JsonTree />`\n\nA value as a tree that expands and collapses, with a preview on every closed node.\n\n### primitives\n\nRequired. `{ View, Text, Pressable }` — see [Primitives](#primitives).\n\n### value\n\nRequired. A `JsonValue`: a string, number, boolean, `null`, an array of those, or an object of them.\nParse a response body before handing it over; this renders a value, it does not accept text.\n\n### rootLabel\n\nOptional. A label for the root row, e.g. `\"response\"`. Omitted, the root renders as its own preview\nwith no key.\n\n### theme\n\nOptional, defaults to `DARK_THEME`. Any `PrettyPrintTheme` — see [Themes](#themes).\n\n### defaultExpanded\n\nOptional, default `true`. Whether the root starts open. Its children always start closed: a\nhundred-entry payload opened all the way is a wall.\n\n### onCopy\n\nOptional. `(text: string) => void`. The clipboard is platform-specific — react-native's is a\nseparate install, the web's is on `navigator` — so the package takes a function. **Without it, the\ncopy actions are not offered at all.** Pass `Clipboard.setStringAsync` or\n`navigator.clipboard.writeText`.\n\n### onRequestMenu\n\nOptional. `(items: MenuItem[], event: unknown) => void`, fired on long-press (React Native) or\nright-click (web). Enables the gesture; without it, no menu is requested. See\n[The context menu](#the-context-menu).\n\n### matcher\n\nOptional. A `Matcher` — see [Search](#search). Drives highlighting **and** expansion: the branches\nholding a match open, everything else collapses.\n\n---\n\n## `<XmlTree />`\n\nA document as a tree. Elements expand and collapse; text and character data are leaves.\n\n### primitives\n\nRequired. As above.\n\n### source\n\nRequired. The raw XML as a string. Parsed in JS, because React Native has no `DOMParser`. A document\nthat will not parse renders a one-line reason instead — it never throws, so keep the raw text around\nto show alongside it.\n\n### theme\n\nOptional, defaults to `DARK_THEME`.\n\n### matcher\n\nOptional. A `Matcher` — see [Search](#search). Matches element names, attribute names, attribute\nvalues, text and character data. An element opens only for a match _inside_ it: one matching on its\nown name or attributes is painted where it sits, since that says nothing about whether its contents\nare worth unfolding.\n\n---\n\n## `<CodeHighlight />`\n\nHighlighted source, one node per token.\n\n### primitives\n\nRequired. As above.\n\n### code\n\nRequired. The source as a string.\n\n### language\n\nRequired. One of the 38 in [Languages](#languages). Use `detectLanguage()` if you have a content\ntype rather than a known language.\n\n### theme\n\nOptional, defaults to `DARK_THEME`.\n\n### format\n\nOptional, default `true`. Re-indents minified source before highlighting, which is the difference\nbetween a readable listing and one endless line. It only applies to languages whose structure is\npunctuation — see [Languages](#languages) for which — and it is a re-indenter, not a pretty-printer:\nit breaks lines on braces, brackets and statement terminators and spaces separators, but it will not\ninsert a space after a keyword or invent a terminator the source omitted. Turn it off for source you\nformatted yourself.\n\n### maxHighlightLength\n\nOptional, defaults to `MAX_HIGHLIGHT_LENGTH` (50,000). Above this many characters the source renders\nas one unstyled block — nothing is truncated, only the highlighting is skipped. Raise it if you'd\nrather wait, or pass `Infinity` to remove the cap:\n\n```tsx\n<CodeHighlight\n  primitives={domPrimitives}\n  code={bundle}\n  language=\"javascript\"\n  maxHighlightLength={Infinity}\n/>\n```\n\nTokenizing walks the string once per rule per position, so cost grows with length times the size of\nthe language's rule table — a megabyte of minified source is enough to block the thread. That is the\ntrade you're making when you raise it.\n\n### matcher\n\nOptional. A `Matcher` — see [Search](#search). Matched runs are painted, including a match that\nspans a token boundary: matching happens once over the whole string and the ranges are clipped per\ntoken, so `x=1` highlights across the identifier, the operator and the number.\n\nNothing here mounts a scroller. A long line needs one, and only you know whether the block is\nalready inside a `ScrollView` or a container with `overflow-x`.\n\n---\n\n## Primitives\n\nEvery renderer takes its components from you. That is what lets one implementation serve both\nplatforms.\n\n```ts\ntype Primitives = {\n  View: ComponentType<{ style?; children? }>;\n  Text: ComponentType<{ style?; children?; selectable? }>;\n  Pressable: ComponentType<{ style?; children?; onPress?; onLongPress? }>;\n};\n```\n\n| Platform     | What to pass                                                                 |\n| ------------ | ---------------------------------------------------------------------------- |\n| React Native | `{ View, Text, Pressable }` imported from `react-native` — no wrapper needed |\n| The DOM      | `domPrimitives`, exported from this package                                  |\n\n`selectable` exists for React Native only, where text is unselectable by default and a code block\nyou cannot copy from is useless; `domPrimitives` drops it rather than forwarding an unknown\nattribute to a `<span>`.\n\nThe style types are deliberately loose: React Native's `StyleProp<ViewStyle>` and React's\n`CSSProperties` are not assignable to each other in either direction, so one concrete type would\nreject a platform outright. The long-press event is `unknown` and passed through untouched for the\nsame reason — anything narrower fails contravariance and would force every React Native consumer to\nwrite a wrapper.\n\nA custom set is a few lines. Anything that accepts a style object and renders children will do.\n\n## The context menu\n\nThe package computes **which actions apply to a node and what each does to the expansion state**;\nyou render the popover. It cannot ship one: a floating menu has to escape its scroll container,\nwhich needs a `Modal` on React Native and a portal or `position: fixed` on the DOM — React Native\nhas no portal and the DOM has no modal, so there is no shared subset to write against.\n\n```tsx\nconst [menu, setMenu] = useState<{ items: MenuItem[]; x: number; y: number } | null>(null);\n\n<JsonTree\n  primitives={{ View, Text, Pressable }}\n  value={response}\n  onCopy={Clipboard.setStringAsync}\n  onRequestMenu={(items, event) => {\n    const { pageX, pageY } = (event as GestureResponderEvent).nativeEvent;\n    setMenu({ items, x: pageX, y: pageY });\n  }}\n/>;\n```\n\n`MenuItem` is `{ label: string; onSelect: () => void }` — render the labels, call `onSelect` on\npress. The items offered depend on the node: _Copy value_ or _Copy object_ when `onCopy` is set,\nplus _Expand_ / _Collapse_, _Expand recursively_ and _Collapse recursively_ on a container that has\nchildren.\n\n`event` is `unknown` because the package never reads it. Cast it at this one call site:\n`nativeEvent.pageX` on React Native, `clientX` on a DOM mouse event.\n\n`example-native/components/ContextMenu.tsx` and `example-web/src/components/ContextMenu.tsx` are\nworking implementations of each — copy one.\n\n## Search\n\nAll three renderers take a `matcher`, which is plain data rather than a callback:\n\n```ts\ntype Matcher = { pattern: RegExp | null; invalid: boolean };\n```\n\nThat shape is the contract. If your app already compiles a matcher for its own list filtering, pass\nit straight in — TypeScript matches structurally, so there is nothing to import and nothing to\nconvert. If you don't have one, `buildMatcher` compiles a query:\n\n```tsx\nimport { buildMatcher, DEFAULT_SEARCH_MODES } from '@axonpack/react-pretty-print';\n\nconst matcher = buildMatcher({ text: query, ...DEFAULT_SEARCH_MODES, matchCase: false });\n\n<JsonTree primitives={domPrimitives} value={data} matcher={matcher} theme={theme} />;\n```\n\n`SearchModes` are `matchCase`, `wholeWord` and `regex`. Two behaviours worth knowing:\n\n- **An uncompilable pattern is no search, not no results.** A half-typed regex gives\n  `{ pattern: null, invalid: true }`, and the tree keeps its normal expansion rather than collapsing\n  to nothing. Read `invalid` if you want to mark the input.\n- **A closed node still previews its contents.** The search walk looks at the leaves underneath it,\n  so a branch with a match opens; a branch without one stays closed and shows its preview as usual.\n\nThe band's colour is the `matchHighlight` token, translucent so the matched text keeps its syntax\ncolour. `findMatches`, `clipMatches`, `splitByMatches` and `testMatch` are exported for building\nyour own row filters against the same matcher.\n\nBoth examples have a search bar at the top with the three mode toggles wired up —\n`example-web/src/components/SearchBar.tsx` and `example-native/components/SearchBar.tsx` — including\nthe invalid-pattern state.\n\n## Themes\n\nA palette is a flat set of colour roles, never a stylesheet, so nothing has to be translated between\nplatforms.\n\n```ts\nimport { AZURE_DARK_VIVID_THEME, DARK_THEME } from '@axonpack/react-pretty-print/themes';\nimport type { PrettyPrintTheme } from '@axonpack/react-pretty-print/themes';\n```\n\nThey are a **separate entry point** on purpose: 130 palettes is ~52KB, and a project that only wants\na renderer shouldn't pull them into its module graph to find out it didn't need them.\n\n| Token                     | Paints                                                                                                                                               |\n| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `background`              | The surface the palette was designed against. **No renderer draws it** — you own your container — but it is stated so you know what you were handed. |\n| `text`                    | Unclassified code text                                                                                                                               |\n| `key`                     | JSON keys, XML element names                                                                                                                         |\n| `string`                  | Quoted strings, XML attribute values                                                                                                                 |\n| `number` / `boolean`      | Literals. They share a colour in every shipped palette: painting `2` and `true` differently is noise.                                                |\n| `null`                    | An absent value                                                                                                                                      |\n| `punctuation`             | Braces, separators, collapsed-node previews                                                                                                          |\n| `toggle`                  | The expand arrow                                                                                                                                     |\n| `keyword`                 | Language keywords                                                                                                                                    |\n| `comment`                 | Comments                                                                                                                                             |\n| `accent`                  | Function names, CSS properties, attribute names                                                                                                      |\n| `tag`                     | Markup tags, CSS selectors                                                                                                                           |\n| `fontFamily` / `fontSize` | Type. Monospace at 12 in every shipped palette.                                                                                                      |\n\n`DARK_THEME` and `LIGHT_THEME` are the neutral pair the renderers default to — the only palettes\nwith no hue cast. The other 128 are named `<hue>_<mode>_<character>_THEME`: 16 hues (`crimson`,\n`amber`, `gold`, `citron`, `lime`, `emerald`, `jade`, `teal`, `cyan`, `azure`, `cobalt`, `sapphire`,\n`indigo`, `violet`, `magenta`, `rose`) × `dark`/`light` × four characters — `muted`, `vivid`, `soft`\nand `crisp`.\n\nEvery palette clears a WCAG contrast ratio against its own background: 4.5:1 for body text, 7:1 on\n`crisp`, never below 3:1 for any token. Writing your own is a plain object; the shipped set's\nconstruction rules are in `src/themes/palettes.const.ts`'s header.\n\n### The monospace font on React Native\n\nEvery shipped palette says `fontFamily: 'monospace'`. That is correct on the web and on Android, and\n**wrong on iOS**: `monospace` is an Android family name, iOS finds no font by it, and it falls back\nto the proportional system font without warning — which stays invisible until something has to line\nup. Nothing here can call `Platform.select`, because importing a platform is the one thing this\ndesign rules out, so override the token:\n\n```tsx\nimport { Platform } from 'react-native';\nimport { DARK_THEME } from '@axonpack/react-pretty-print/themes';\n\nconst MONOSPACE = Platform.select({ ios: 'Menlo', default: 'monospace' });\nconst theme = { ...DARK_THEME, fontFamily: MONOSPACE };\n```\n\nIt has to be a single family name, not a CSS stack: React Native looks the string up verbatim, so\n`\"Menlo, monospace\"` matches nothing on either platform. `Menlo` ships with every iOS.\n`example-native/fonts.ts` is this, and nothing on the DOM needs it.\n\n## Languages\n\n38, exported as `SUPPORTED_LANGUAGES`.\n\n| Group                 | Languages                                                                                                              | `format` re-indents |\n| --------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------- |\n| Braces, `//`, `/* */` | `javascript` `typescript` `tsx` `java` `kotlin` `swift` `go` `rust` `c` `cpp` `csharp` `php` `dart` `scala` `protobuf` | yes                 |\n| `#` comments          | `python` `ruby` `bash` `dockerfile` `makefile` `graphql`                                                               | `graphql` only      |\n| Key/value             | `json` `json5` `yaml` `toml` `ini` `properties`                                                                        | `json` and `json5`  |\n| Angle brackets        | `html` `xml` `svg` `markdown`                                                                                          | no                  |\n| Stylesheets           | `css` `scss` `less`                                                                                                    | yes                 |\n| Line-oriented         | `sql` `diff` `log`                                                                                                     | `sql` only          |\n| Unstyled              | `plain`                                                                                                                | no                  |\n\nIndentation-sensitive and line-oriented formats are excluded from `format` deliberately: `python`\nand `yaml` carry meaning in the whitespace they already have, markup and markdown have their own\nshape, and a diff or a log is already one record per line.\n\n## Utilities\n\nEverything the renderers use is exported, so a caller that wants its own rendering can take the\nparsed output and skip the components.\n\n| Export                   | Signature                                                       | Notes                                                                                                            |\n| ------------------------ | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `detectLanguage`         | `(mimeType: string \\| undefined, body: string) => Language`     | Reads the content type, then sniffs the body. Use it when a server labels everything `text/plain`.               |\n| `tokenize`               | `(code: string, language: Language) => Token[]`                 | `{ type, text }` per token; concatenating `text` returns the input unchanged. This is the renderer escape hatch. |\n| `formatCode`             | `(code: string, language: Language) => string`                  | Re-indents, or returns the input for a language it does not format.                                              |\n| `SUPPORTED_LANGUAGES`    | `Language[]`                                                    | Sorted.                                                                                                          |\n| `MAX_HIGHLIGHT_LENGTH`   | `50_000`                                                        | Above this `CodeHighlight` renders unhighlighted rather than blocking.                                           |\n| `parseXml`               | `(source: string) => { root: XmlElement } \\| { error: string }` | Never throws.                                                                                                    |\n| `isExpandable`           | `(value: JsonValue) => boolean`                                 | True for any object or array.                                                                                    |\n| `hasChildren`            | `(value: JsonValue) => boolean`                                 | False for `{}` and `[]`, which is why they get no toggle.                                                        |\n| `isPlainObject`          | `(value: JsonValue) => boolean`                                 | `null` is not one.                                                                                               |\n| `buildPreview`           | `(value: object \\| array) => string`                            | The summary a closed node shows.                                                                                 |\n| `chunkArrayRange`        | `(length: number) => [number, number][]`                        | The bucket boundaries for a long array.                                                                          |\n| `collectExpandablePaths` | `(path: string, value: JsonValue) => string[]`                  | Every path under a node, for expanding a subtree.                                                                |\n| `formatCopyValue`        | `(value: JsonValue) => string`                                  | `JSON.stringify` at two spaces.                                                                                  |\n| `ARRAY_CHUNK_SIZE`       | `10`                                                            | Arrays longer than this are bucketed.                                                                            |\n\n## Types\n\n`JsonValue`, `Language`, `Token`, `TokenType`, `MenuItem`, `Matcher`, `MatchRange`, `SearchModes`,\n`SearchQuery`, `TextSegment`, `Primitives`, `JsonTreeProps`,\n`XmlTreeProps`, `CodeHighlightProps`, `XmlNode`, `XmlElement`, `XmlText`, `XmlCData`,\n`XmlParseResult` from the root; `PrettyPrintTheme` from `/themes`.\n\n`TokenType` is one of `keyword`, `string`, `comment`, `number`, `function`, `tag`, `attr-name`,\n`attr-value`, `property`, `selector`, `punctuation`, `plain`.\n\n## What's built, and what isn't\n\n[The notes](./notes/README.md) — one per area, each owning its own feature list, what is not built\nyet, and what the platforms make impossible, along with the design decisions behind it:\n[Primitives](./notes/primitives.md) · [JSON](./notes/json.md) · [XML](./notes/xml.md) ·\n[Code](./notes/code.md) · [Themes](./notes/themes.md).\n\n## License\n\nMIT © [Md Asadujjaman](https://github.com/abappi19).\n","readmeFilename":"README.md","_rev":"1-a60de284e34be5ab1e78733fdbcc8f0b"}