Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

JSON Crack Admin & Developer Guide

Generated June 2026 from AykutSarac/jsoncrack.com at f67c540

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

This guide is the architectural and operational reference for JSON Crack — the open-source data visualizer at jsoncrack.com. It is written for engineers self-hosting the web app, developers integrating the jsoncrack-react component, contributors working in the monorepo, and architects evaluating how the system is built. End-user task documentation lives in the companion User Guide.

JSON Crack is a Turborepo + pnpm monorepo that produces four artifacts from one shared rendering core: a Next.js web application (statically exported and served by nginx), a VS Code extension, a Chrome extension, and a published React component. There is no backend service — all parsing, conversion, querying, and rendering happen client-side.


1. Executive Summary

Attribute Value
Project JSON Crack — "the open-source JSON Editor"
License Apache-2.0 (monorepo & web app); MIT (VS Code extension)
Repository AykutSarac/jsoncrack.com
Architecture Turborepo monorepo, 3 apps + 1 published package
Primary deliverable apps/www — Next.js 16 app, static export, served by nginx on port 8080
Rendering core packages/jsoncrack-react — React + reaflow (ELK layout) canvas
State management Zustand
UI toolkit Mantine 8 + styled-components
Runtime requirements Node.js ≥ 24, pnpm ≥ 10 (build-time only; output is static)
Backend / database None — fully client-side; no server-side data storage
Analytics Google Analytics via nextjs-google-analytics (hosted site only)

Operational profile. Because the web app is a static export, "operating" it means serving a directory of HTML/JS/WASM assets behind any static web server. The provided Docker image bakes the build into nginxinc/nginx-unprivileged. There are no migrations, no secrets, no stateful services, and no horizontal-scaling concerns beyond standard static-asset CDN practices.


2. Architecture

2.1 Monorepo layout

jsoncrack.com/                    (Turborepo root, pnpm workspaces)
├── apps/
│   ├── www/                      Next.js web app  → jsoncrack.com (PRIMARY deliverable)
│   ├── vscode/                   VS Code extension (jsoncrack-vscode v5.1.0)
│   └── chrome-extension/         Chrome MV3 extension (v0.1.2)
├── packages/
│   └── jsoncrack-react/          Published npm package (v1.0.3) — the canvas
├── turbo.json                    Task graph (build/dev/lint/test)
├── pnpm-workspace.yaml           Workspace globs
└── package.json                  Root scripts (turbo run ...)

Workspace dependencies are wired with pnpm's workspace:* protocol: all three apps depend on jsoncrack-react as a local workspace package, so the canvas is built once and consumed everywhere.

2.2 Data flow (web app)

                 ┌─────────────────────────── apps/www (browser) ───────────────────────────┐
 user input  ──▶ │  TextEditor (Monaco)                                                       │
 (type/paste/    │      │ onChange                                                            │
  import/URL)    │      ▼                                                                      │
                 │  useFile (Zustand)  ──contentToJson()──▶  useJson (Zustand)                 │
                 │      │  (jsonAdapter: js-yaml / fast-xml-parser / json-2-csv / jsonc)       │
                 │      ▼ debounced 400ms                                                      │
                 │  GraphView ──▶ <JSONCrack json={...}>  (jsoncrack-react)                    │
                 │                     │ parseGraph()  (jsonc-parser → nodes/edges)            │
                 │                     ▼                                                        │
                 │                  reaflow <Canvas> + ELK layout  ──▶  SVG graph              │
                 │                     ▲                                                        │
                 │  useGraph (Zustand) ─┘  (viewport, direction, collapse, ref API)            │
                 └────────────────────────────────────────────────────────────────────────────┘

Key points:

  • useFile owns the raw text and its format; it converts content to a normalized JSON object via jsonAdapter, then pushes a stringified, pretty-printed JSON into useJson on a 400 ms debounce.
  • useJson holds the canonical JSON string that the graph renders.
  • GraphView subscribes to useJson and renders the JSONCrack component from the workspace package, wiring viewport/collapse callbacks into useGraph.
  • The actual graph construction (parseGraph) and layout (ELK via reaflow) live entirely inside jsoncrack-react.

2.3 The four products and what they share

                     packages/jsoncrack-react  (the canvas: parser + reaflow + theme)
                          ▲            ▲              ▲
        ┌─────────────────┘            │              └────────────────────┐
   apps/www                       apps/vscode                       apps/chrome-extension
   Next.js editor: full          Webview (Vite+React) driven        Content script injects a
   toolset, stores, tools,       by an esbuild extension host       Raw|Graph toggle on JSON
   conversion, export, embed     via postMessage                    pages; mounts the canvas

Each product wraps the same <JSONCrack> component but supplies its own data source and chrome:

  • www — rich editor with Monaco, Zustand stores, conversion, query tools, export, and embedding.
  • vscode — a Vite-built React webview that receives the document text from the extension host over postMessage.
  • chrome-extension — a content script that detects JSON pages and lazy-loads the canvas into an overlay.

3. Technology Stack & Requirements

3.1 Build/runtime requirements

Tool Version Scope
Node.js ≥ 24 (root engines) Build only
pnpm ≥ 10 (pinned pnpm@10.20.0) Package management
Turbo 2.8.x Task orchestration
VS Code ≥ 1.100.0 VS Code extension host
React (peer) ≥ 18 jsoncrack-react consumers

The hosted web app is a static export — once built there is no Node runtime in production (nginx serves files).

3.2 Web app (apps/www) key dependencies

Dependency Role
next 16 Framework; configured for static export (output: "export")
react / react-dom 19 UI runtime
@mantine/core, @mantine/hooks, @mantine/dropzone, @mantine/code-highlight UI components, hooks, file dropzone, code highlighting
styled-components Theming (light/dark token objects)
@monaco-editor/react The text editor pane
jsoncrack-react (workspace) The graph canvas
reaflow Graph rendering + ELK layout (transitive via the package)
react-json-tree The Tree view
allotment Resizable split panes
zustand State stores
jsonc-parser Tolerant JSON parsing
js-yaml, fast-xml-parser, json-2-csv Format conversion
jsonpath-plus JSON Path tool
jq-web In-browser jq (WASM)
json_typegen_wasm, gofmt.js Type generation (WASM) + Go formatting
html-to-image PNG/JPEG/SVG export
react-hot-toast Toasts
nextjs-google-analytics, next-seo, next-sitemap Analytics, SEO, sitemap

3.3 Extension stacks

  • VS Code: Vite (webview bundle) + esbuild (extension host bundle) + React 19 + Mantine + Shiki (syntax highlight).
  • Chrome: Vite library build (IIFE) emitting a single content-script.js; React 19 + jsoncrack-react.

4. Configuration Reference

JSON Crack has very little runtime configuration — by design, since it's a static client-side app. All configuration is build-time environment variables in apps/www.

4.1 Environment variables

Variable Defined in Default Effect
NEXT_PUBLIC_NODE_LIMIT apps/www/.env 1200 (prod), 2000 (dev) Maximum renderable graph nodes; exceeding it shows the "too large" overlay. Read into SUPPORTED_LIMIT (constants/graph.ts) and passed as maxRenderableNodes.
NEXT_PUBLIC_DISABLE_EXTERNAL_MODE apps/www/.env.development false (dev) When true, suppresses the ToDiagram "Upgrade to Pro" link, the editor-choice modal, and the external-mode dialog. Set this for clean self-hosting.
NEXT_TELEMETRY_DISABLED apps/www/.env 1 Disables Next.js build telemetry.
ANALYZE build env unset When true, wraps the build with @next/bundle-analyzer.
NODE_ENV docker-compose production Standard Node environment flag.

Important

All public env vars are inlined at build time (the NEXT_PUBLIC_ prefix). Changing the node limit requires rebuilding the static export — it cannot be changed on an already-built deployment.

Note

NEXT_PUBLIC_NODE_LIMIT (a node count) and the ~300 KB upload guard in FullscreenDropzone/useFile (a byte size) are two independent limits. Raising one does not raise the other.

4.2 next.config.js

const config = {
  output: "export",                       // static HTML export — no Node server in prod
  reactStrictMode: false,
  productionBrowserSourceMaps: true,
  compiler: { styledComponents: true },   // SSR-safe styled-components
  turbopack: { resolveAlias: { fs: { browser: "./shims/empty.ts" } } },
  webpack: (config, { isServer }) => {
    config.resolve.fallback = { fs: false };
    config.output.webassemblyModuleFilename = "static/wasm/[modulehash].wasm";
    config.experiments = { asyncWebAssembly: true, layers: true };  // for jq-web / json_typegen
    if (!isServer) config.output.environment = { ...config.output.environment, asyncFunction: true };
    return config;
  },
};

Notable choices: static export (output: "export") means the app ships as a folder of files; async WebAssembly is enabled so jq-web and json_typegen_wasm load; and the fs module is stubbed for the browser.

4.3 Turborepo task graph (turbo.json)

Task dependsOn Cached outputs
build ^build .next/**, dist/**, out/**, build/**
dev ^build not cached, persistent
lint ^build none
test ^build none
analyze ^build .next/**, out/**, build/**

The ^build dependency ensures the jsoncrack-react package is built before any app that consumes it.


5. Deployment & Self-Hosting

The Docker assets live in apps/www. The image is a multi-stage build that prunes the monorepo to just the www workspace, installs, builds the static export, and serves it from an unprivileged nginx.

# Dockerfile (apps/www) — abridged
FROM node:24-alpine AS base
RUN apk add --no-cache libc6-compat && npm install -g pnpm@10.20.0

FROM base AS pruner
COPY . .
RUN pnpm dlx turbo@2.8.20 prune www --docker      # isolate the www workspace

FROM base AS installer
COPY --from=pruner /app/out/json/ .
RUN pnpm install --frozen-lockfile --ignore-scripts
COPY --from=pruner /app/out/full/ .
RUN pnpm turbo run build                          # produces apps/www/out (static)

FROM nginxinc/nginx-unprivileged:stable AS production
COPY --from=installer /app/apps/www/out /app
COPY apps/www/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 8080
# docker-compose.yml (apps/www)
services:
  jsoncrack:
    image: jsoncrack
    container_name: jsoncrack
    build: { context: ., dockerfile: Dockerfile }
    ports: ["8888:8080"]
    environment: [NODE_ENV=production]

Build and run:

cd apps/www
docker compose build
docker compose up
[+] Running 1/1
 ✔ Container jsoncrack  Started

The app is then served at http://localhost:8888 (container port 8080).

Tip

The base image is nginxinc/nginx-unprivileged, which runs nginx as a non-root user and listens on 8080 (not 80). Map it to whatever host port you like; the compose file uses 8888.

5.2 nginx routing

Because Next.js static export emits per-route HTML files, nginx maps the SPA routes explicitly:

server {
    listen 8080;
    root  /app;
    include /etc/nginx/mime.types;

    location /editor { try_files $uri /editor.html; }
    location /widget { try_files $uri /widget.html; }
    location /docs   { try_files $uri /docs.html; }
}

5.3 Deploy checklist

  • [ ] Node ≥ 24 and pnpm ≥ 10 available on the build host (or use the Docker node:24-alpine base).
  • [ ] Set NEXT_PUBLIC_NODE_LIMIT for your hardware before building.
  • [ ] Set NEXT_PUBLIC_DISABLE_EXTERNAL_MODE=true to remove ToDiagram promos in a self-hosted/branded deployment.
  • [ ] Build the static export (pnpm build:www or the Docker installer stage).
  • [ ] Serve apps/www/out from any static web server (the provided nginx config handles /editor, /widget, /docs).
  • [ ] Front with a CDN/reverse proxy for TLS and caching of the immutable static/ assets.

5.4 Build outputs

Command Output
pnpm build:www apps/www/out/ (static HTML/JS/WASM)
pnpm build:vscode apps/vscode/build/ (webview + extension.js)
pnpm build:chrome apps/chrome-extension/dist/ (content-script.js + manifest)
pnpm --filter jsoncrack-react build packages/jsoncrack-react/dist/ (ESM + .d.ts + CSS)

6. Web App Internals (apps/www)

6.1 Pages (routes)

Route File Purpose
/ pages/index.tsx Marketing landing page (hero, features, FAQ); fetches GitHub star count at build via getStaticProps
/editor pages/editor.tsx The full editor (split pane, toolbar, modals)
/widget pages/widget.tsx Embeddable graph-only view driven by postMessage and ?json=
/docs pages/docs.tsx Embedding documentation (iframe / postMessage / React)
/legal/privacy, /legal/terms pages/legal/* Legal pages rendered from data/*.json
/404, /_error pages/404.tsx, pages/_error.tsx Error pages

6.2 State stores (Zustand)

Store File Responsibility
useFile store/useFile.ts Raw text, format, validation error, JSON Schema; converts content↔JSON via jsonAdapter; session-storage persistence (<80 KB, non-iframe); URL fetch; debounced push to useJson
useJson store/useJson.ts The canonical JSON string + loading flag that the graph consumes
useConfig store/useConfig.ts UI prefs — darkmodeEnabled, liveTransformEnabled, gesturesEnabled, rulersEnabled; persisted to localStorage key config
useModal store/useModal.ts Open/closed state for each named modal (Import, Download, JQ, JPath, Schema, Type, NodeModal)
useGraph features/editor/views/GraphView/stores/useGraph.ts Viewport, layout direction, fullscreen, selected node, collapsed count; proxies imperative calls (zoomIn, centerView, collapseAll, …) to the JSONCrackRef

6.3 Format conversion (lib/utils/jsonAdapter.ts)

A single contentToJson(value, format) / jsonToContent(json, format) pair powers all conversion. Parsers are dynamically imported so each format's library only loads when used:

Format Parse Serialize
JSON jsonc-parser (tolerant) JSON.stringify
YAML js-yaml load js-yaml dump
XML fast-xml-parser XMLParser ($ attr prefix) XMLBuilder
CSV json-2-csv csv2json json2csv

Switching the bottom-bar format selector calls useFile.setFormat, which round-trips the content through the previous format's parser and the new format's serializer.

6.4 The graph wiring (GraphView)

features/editor/views/GraphView/index.tsx renders <JSONCrack> from the package and binds:

  • json ← useJson.json
  • theme ← useConfig.darkmodeEnabled
  • layoutDirection ← useGraph.direction
  • showGrid ← useConfig.rulersEnabled
  • trackpadZoom ← useConfig.gesturesEnabled
  • maxRenderableNodes ← SUPPORTED_LIMIT (or 1500 fallback)
  • onNodeClick → opens NodeModal and stores the selected node
  • renderNodeLimitExceeded → the NotSupported overlay

6.5 Tools (the Tools menu)

Tool Implementation
JSON Query (jq) hooks/useJsonQuery.ts → dynamic import("jq-web"), jq.promised.json()
JSON Path JPathModal → jsonpath-plus JSONPath({ path, json })
JSON Schema SchemaModal → sets useFile.jsonSchema; Monaco validates against it (TextEditor registers it via jsonDefaults.setDiagnosticsOptions)
Generate Type TypeModal → dynamic import("json_typegen_wasm") for TS/Kotlin/Rust/JSON-Schema; lib/utils/json2go.js + gofmt.js for Go

6.6 Search (useFocusNode)

hooks/useFocusNode.ts runs a debounced (300 ms) DOM query for span[data-key*='<term>' i], highlights matches via CSS classes (lib/utils/search.ts), and flies the camera to the active match using the viewport's centerFitElementIntoView. It exposes next/prev/clear and match counters consumed by SearchInput.


7. jsoncrack-react — Full API Reference

The published package (packages/jsoncrack-react, v1.0.3) is the reusable canvas. Its public surface is its barrel export:

// packages/jsoncrack-react/src/index.ts
export { JSONCrack } from "./JSONCrackComponent";
export { parseGraph } from "./parser";
export type { JSONCrackProps, JSONCrackRef } from "./JSONCrackComponent";
export type { ParseGraphResult } from "./parser";
export type { CanvasThemeMode, EdgeData, GraphData, LayoutDirection, NodeData, NodeRow } from "./types";

7.1 <JSONCrack> props

Prop Type Default Description
json string | object — (required) Data to visualize. Objects are stringified (memoized per reference).
theme "light" | "dark" "dark" Canvas & node color theme.
layoutDirection "LEFT" | "RIGHT" | "DOWN" | "UP" "RIGHT" ELK node placement direction.
showControls boolean true Render the built-in zoom/focus control overlay.
showGrid boolean true Draw the background grid.
trackpadZoom boolean false Treat two-finger trackpad gestures like touch (pinch-zoom).
centerOnLayout boolean true Auto fit-to-center after each ELK layout pass.
maxRenderableNodes number 1500 Hard cap; exceeding it triggers the limit overlay.
className string — Extra class on the canvas wrapper.
style CSSProperties — Extra inline style merged onto the wrapper.
onNodeClick (node: NodeData) => void — Fired when a node is clicked.
onParse (graph: GraphData) => void — Fired with nodes/edges after each successful parse.
onParseError (error: Error) => void — Fired on any parse/graph-construction error.
onViewportCreate (viewPort: ViewPort) => void — Fired once the internal viewport is created.
renderNodeLimitExceeded (count, max) => ReactNode — Custom overlay when the node limit is exceeded.
collapsedPaths string[] — Controlled collapsed-path set (serialized keys). Requires onToggleCollapse.
onToggleCollapse (path: JSONPath) => void — Mutator for controlled collapse mode.
onCollapseChange (paths: string[]) => void — Observe collapsed-path set in uncontrolled mode.

7.2 JSONCrackRef (imperative handle)

Obtain via a ref; methods control the viewport and collapse state:

Method Description
zoomIn() / zoomOut() Step zoom while keeping center fixed
setZoom(factor) Set absolute zoom at current center
centerView() Fit the whole graph in the viewport
focusFirstNode() Center/zoom on the root node
toggleCollapse(path) Toggle one JSON path (uncontrolled mode)
collapseAll() / expandAll() Collapse first-level containers / clear collapse
getCollapsedPaths() Current collapsed-paths snapshot

7.3 parseGraph(json: string): ParseGraphResult

The pure parser (also exported standalone). Uses jsonc-parser's parseTree and walks it into { nodes, edges, errors }. Arrays and objects become container nodes; primitive rows are grouped into their parent node's text rows. Node sizes are computed via calculateNodeSize. Returns collected ParseError[] rather than throwing.

7.4 Core types

type LayoutDirection = "LEFT" | "RIGHT" | "DOWN" | "UP";
type CanvasThemeMode = "light" | "dark";

interface NodeRow { key: string | null; value: string|number|null|boolean; type: Node["type"]; childrenCount?: number; to?: string[]; }
interface NodeData { id: string; text: NodeRow[]; width: number; height: number; path?: JSONPath; parentKey?: string; parentType?: string; }
interface EdgeData { id: string; from: string; to: string; text: string | null; }
interface GraphData { nodes: NodeData[]; edges: EdgeData[]; }

7.5 Theming

theme.ts exports a themes record keyed by "light"/"dark". canvasHelpers.buildCanvasStyle projects those tokens onto CSS custom properties (--node-key, --node-value, --node-integer, etc.) on the canvas wrapper, so theming is pure CSS variables — no re-render of nodes is needed to recolor.

7.6 Rendering & layout internals

  • The canvas uses reaflow's <Canvas> with a fixed layoutOptions ELK config (NETWORK_SIMPLEX node placement, edge-length compaction). It runs in readonly, non-pannable/non-zoomable mode; panning/zooming is delegated to react-zoomable-ui's <Space>.
  • Collapse is computed in JS: collapsed paths are serialized JSON arrays; nodes whose path is prefixed by a collapsed path are filtered out before being handed to reaflow.
  • Fit-to-view (canvasHelpers.fitGraphToViewPort) measures the painted graph via a four-step cascade (union of leaf client rects → content-group rect → getBBox through CTM → ELK layout box) and centers the viewport on it.
  • After a collapse toggle, a requestAnimationFrame loop re-pins the clicked button under the cursor once the layout stabilizes, avoiding a visual jump.

8. VS Code Extension Internals (apps/vscode)

The extension has two halves: an extension host (Node, bundled by esbuild → build/extension.js) and a webview (React app bundled by Vite → build/webview/).

8.1 Commands & contribution points

Declared in apps/vscode/package.json:

Command ID Title Trigger
jsoncrack-vscode.start Enable JSON Crack visualization Editor title-bar icon (shown for .json / editorLangId == json)
jsoncrack-vscode.start.selected Open with JSON Crack Editor context menu, when there's a selection
jsoncrack-vscode.start.specific Enable JSON Crack visualization for specific file Programmatic (hidden from palette)

Activation event: workspaceContains:**/*.{json}.

8.2 Host ↔ webview messaging

ext-src/extension.ts registers the commands; each creates a webview panel (ext-src/webview.ts) beside the active editor and streams the document text in:

extension host                         webview (App.tsx)
  createWebviewPanel() ───────────────▶ render <JSONCrack json={...}>
  onDidReceiveMessage("ready") ◀──────── postMessage("ready")
  postMessage({ json }) ──────────────▶ setJson(...)  (re-renders graph)
  onDidChangeTextDocument ────────────▶ postMessage({ json }) (live updates)

createWebviewForSelectedText sends only the selected range and keeps it in sync on edits. The webview reads the editor theme from document.body[data-vscode-theme-kind] and forces the matching Mantine color scheme.

8.3 Webview security (CSP)

webview.ts builds the panel HTML with a strict Content-Security-Policy and a per-load nonce:

default-src 'self' <cspSource> blob:
connect-src <cspSource> blob:
script-src 'unsafe-eval' 'unsafe-inline' <cspSource>
style-src  <cspSource> 'unsafe-inline'
worker-src <cspSource> blob: data:

enableScripts: true and retainContextWhenHidden: true are set; resources are restricted to the build/webview and assets directories. 'unsafe-eval' is required for the ELK/WASM layout path.


9. Chrome Extension Internals (apps/chrome-extension)

A Manifest v3 extension (v0.1.2) consisting of a single content script.

9.1 Manifest

{
  "manifest_version": 3,
  "name": "JSON Crack",
  "content_scripts": [
    { "matches": ["<all_urls>"], "js": ["content-script.js"], "run_at": "document_idle" }
  ]
}

It declares no host_permissions beyond the content-script match and no background service worker.

9.2 Behavior

src/content-script.tsx:

  1. Runs only in the top frame, only when document.contentType includes json and the body parses as JSON.
  2. Injects a floating Raw | Graph segmented control plus a hidden overlay container, and inlines both the package stylesheet and a local stylesheet.
  3. On Graph, lazily import("jsoncrack-react") and mounts <JSONCrack> into the overlay; on Raw (or Esc) it unmounts.
  4. Clicking a node opens a self-contained modal (content + JSON path, each with copy).
  5. Over-limit graphs render a "too large" upgrade card.

9.3 CSP/Worker workaround

Many JSON responses are served under a strict default-src 'none' CSP, which blocks ELK's attempt to spawn a Web Worker. The script temporarily shadows globalThis.Worker = undefined during the dynamic import and initial layout so ELK falls back to its synchronous path, then restores Worker so the host page is unaffected. The Vite build also injects a banner defining Worker = undefined and a minimal process.env.


10. Security Analysis

Area Assessment
Data residency All parsing/conversion/rendering is client-side. The web app is a static export with no backend; documents are never uploaded. Matches the public privacy claims.
Persistence Web app: editor content cached in sessionStorage (only <80 KB, non-iframe) and UI prefs in localStorage (config). No cookies beyond a 1-day editor_choice_shown marker. Chrome extension: no persistent storage of page JSON.
Network egress Web app: outbound requests only when the user supplies a URL (?json= / Import) or on the landing page's build-time GitHub star fetch; plus Google Analytics on the hosted site. Monaco is loaded from unpkg.com CDN. Extensions: VS Code is fully offline; Chrome makes no automatic transmission of page JSON.
dangerouslySetInnerHTML TreeView/TextRenderer.tsx linkifies URL-like strings by building <a> markup and injecting it via dangerouslySetInnerHTML. Inputs are user-supplied data values. This is a deliberate feature but is the single most security-sensitive code path; downstream forks should sanitize if rendering untrusted data in a shared context.
Webview CSP The VS Code webview uses a strict CSP with a per-load nonce and locked-down resource roots, but allows 'unsafe-eval'/'unsafe-inline' (required for the WASM layout engine).
Secrets None. There are no API keys, auth tokens, or server credentials anywhere in the deployable.

Caution

If you fork JSON Crack to visualize untrusted, attacker-controlled data inside a privileged page, review the TextRenderer linkify path — it interpolates string values into HTML. The upstream use case (a sandboxed editor showing the user's own data) makes this low-risk, but the calculus changes in an embedded/multi-tenant context.


11. Performance Tuning

Lever Where Effect
maxRenderableNodes / NEXT_PUBLIC_NODE_LIMIT prop / env Caps graph size before the layout engine is invoked; the primary guard against jank.
Live Transform off bottom bar (useConfig.liveTransformEnabled) Stops per-keystroke re-parse/re-layout for large documents.
Debounced JSON push useFile (400 ms) Coalesces rapid edits before re-rendering the graph.
Memoized parse & style jsoncrack-react toJsonText caches stringified objects (WeakMap); class/style memoized so reaflow doesn't see new objects each render.
Collapse before fit JSONCrackComponent Hidden nodes are filtered out before reaflow/ELK runs, reducing layout cost on large graphs.

Tip

ELK layout cost grows superlinearly with node count. For consistently large data, lower NEXT_PUBLIC_NODE_LIMIT rather than raising it — the overlay is a better UX than a multi-second freeze.


12. Operations

12.1 Common commands (from repo root)

# Web app
pnpm dev:www          # next dev on :3000
pnpm build:www        # static export → apps/www/out

# VS Code extension
pnpm dev:vscode       # watch build
pnpm build:vscode

# Chrome extension
pnpm dev:chrome       # vite build --watch → apps/chrome-extension/dist
pnpm build:chrome

# All workspaces
pnpm dev | pnpm build | pnpm lint | pnpm test

12.2 Loading the Chrome extension for testing

1. pnpm build:chrome
2. chrome://extensions  →  enable Developer mode
3. Load unpacked  →  select apps/chrome-extension/dist

12.3 Debugging the VS Code extension

  1. Open the monorepo root in VS Code.
  2. Press F5 and pick Run VSCode Extension (apps/vscode).
  3. In the Extension Development Host, open a .json file and run JSON Crack: Enable JSON Crack visualization. Reload the host with Ctrl/⌘+R after changes.

12.4 Tests

Only jsoncrack-react ships unit tests (Vitest): parser.test.ts, canvasHelpers.test.ts, CollapseContext.test.ts. Run with pnpm --filter jsoncrack-react test. The apps have no automated test suite — contributions are verified manually with screenshots/recordings per the contributing guide.


13. Code Patterns & Conventions

  • Workspace package as shared core. The graph lives in one published package consumed by all apps via workspace:*; never duplicate rendering logic into an app.
  • Zustand slices by concern. Separate stores for file, json, config, modal, and graph rather than one global store; useConfig is the only persisted one.
  • Dynamic imports for heavy/optional code. jq-web, json_typegen_wasm, js-yaml, fast-xml-parser, json-2-csv, and the editor/modals are all import()-ed on demand to keep the initial bundle small.
  • Controlled vs. uncontrolled collapse. jsoncrack-react supports both; provide collapsedPaths + onToggleCollapse to control it, or omit them to let the component manage state internally.
  • Theme via CSS custom properties. Recoloring is a style change, not a React re-render.
  • Code style: Google TypeScript Style Guide; ESLint + Prettier with @trivago/prettier-plugin-sort-imports. Lint runs tsc first.

14. Known Issues & Potential Improvements

Drawn from the community issue tracker and code observations:

Theme Detail Source
Large-file handling Big JSON (e.g. >2,500 lines) hits the node limit; requests for partial/streamed rendering recur. #513, #234, #337
Numeric precision int64+ values lose precision through JS parsing. #462
Paste truncation Pasting can truncate JSON at certain positions. #463
Blank-line handling Confusion over whitespace in otherwise-valid JSON. #509
SVG→PDF export SVG export can require external resources, breaking PDF conversion; PNG is the reliable path. #421
Offline editor Requests for a fully offline/CSP-friendly self-hosted editor and a published Docker image. #368, #226, #459, #434
Format support Requests for JSON5, .xlsx import, encoding selection. #331, #414, #464
Feature requests Copy-JSON in tools menu, jump-to-code from diagram, table view of nested JSON, per-node colors. #576, #570, #343, #417
Monaco from CDN TextEditor loads Monaco from unpkg.com — a hard external dependency that breaks in air-gapped/offline deployments. code

Note

Several "offline" requests are partially addressed by the documented Docker self-host path, but the hosted editor's Monaco CDN dependency (unpkg.com) and Google Analytics remain external touchpoints to remove for a truly air-gapped build.


15. Contributing

The project uses an issue-first workflow: open or find an issue before coding, get it approved, then submit a PR linked with Closes #123. PRs must include a clear description and visual evidence (screenshot or recording), pass local pnpm dev verification, and follow the Google TypeScript Style Guide. The performance guideline is explicit: avoid unnecessary re-renders. See CONTRIBUTING.md and CODE_OF_CONDUCT.md in the repo.


16. Quick Reference

Ports & paths

Item Value
nginx listen port (container) 8080
docker-compose host port 8888
Web app static output apps/www/out
Dev server http://localhost:3000

Environment variables

Var Default
NEXT_PUBLIC_NODE_LIMIT 1200 (prod) / 2000 (dev)
NEXT_PUBLIC_DISABLE_EXTERNAL_MODE false
NEXT_TELEMETRY_DISABLED 1
ANALYZE unset

Package versions

Package Version
jsoncrack-vscode 5.1.0
jsoncrack-react 1.0.3
chrome-extension 0.1.2
next 16
react 19

Workspace map

Path Artifact
apps/www Next.js static site
apps/vscode VS Code extension
apps/chrome-extension Chrome MV3 extension
packages/jsoncrack-react npm package (the canvas)