§14/§15: rename CustomNode public API around node-vs-component#324
Merged
Conversation
Aligns the CustomNodeDefinition surface on one distinction: a node is a position in the data tree; a component is the React function that renders it. Renames (breaking, v2): - element → component, customKey → keyComponent, wrapperElement → wrapperComponent - customNodeProps → componentProps (feeds component + keyComponent) - hideKey → showKey (polarity inverted; default true) - showInTypesSelector → showInTypeSelector - type CustomNodeProps → CustomComponentProps (resolves the CustomNodeProps/CustomNodeDefinition name clash) Also: new CustomWrapperProps types wrapperComponent, which now receives its config as wrapperProps (was delivered as customNodeProps) — the only genuine set≠read asymmetry. Internal CustomNodeData FC fields gain the Custom prefix (CustomComponent/CustomWrapperComponent/CustomKeyComponent). CustomNodeDefinition and CustomKeyProps keep their names. Updates all 12 @json-edit-react/components definitions + bodies, the demo and custom-component-library consumers (demo/src/v1 left on published v1), README custom-nodes section + a light node-vs-component terminology pass, and adds migration-guide §13. Tests: new test/customNode.test.tsx (23 tests) pinning the DOM/setData contract of every CustomNodeDefinition field, including a wrapperProps-delivery regression guard. These caught a polarity regression — non-custom nodes hid their keys because showKey defaulted only inside getCustomNode; fixed by defaulting showKey = true at the consumption sites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundle size impact
|
| Format | Base raw | PR raw | Δ raw | Base gzip | PR gzip | Δ gzip |
|---|---|---|---|---|---|---|
| esm | 54.11 KB | 54.18 KB | 🔺 +70 B (+0.13%) | 18.95 KB | 18.94 KB | 🟢 -6 B (-0.03%) |
| cjs | 55.59 KB | 55.66 KB | 🔺 +70 B (+0.12%) | 19.01 KB | 19.01 KB | 🟢 -4 B (-0.02%) |
@json-edit-react/components
| Format | Base raw | PR raw | Δ raw | Base gzip | PR gzip | Δ gzip |
|---|---|---|---|---|---|---|
| esm | 11.52 KB | 11.51 KB | 🟢 -2 B (-0.02%) | 3.91 KB | 3.91 KB | 🟢 -3 B (-0.07%) |
| cjs | 12.09 KB | 12.09 KB | 🟢 -2 B (-0.02%) | 3.88 KB | 3.88 KB | 🟢 -2 B (-0.05%) |
Measured from build/index.{cjs,esm}.js. Gzip at level 9.
CarlosNZ
commented
Jun 4, 2026
| > Same mental model as `useState<T>`: `T` describes the data you provided, not a runtime invariant. If structural edits are unrestricted, post-edit values may not conform to `T` — pair with `allowAdd` / `allowDelete` / `allowTypeSelection`, or validate in `onUpdate`, if you depend on the shape. | ||
|
|
||
| `CustomNodeDefinition` is intentionally **not** generic on the data type — its two existing generics (for `customNodeProps` and wrapper props) are unchanged, and custom-node `condition` / `element` continue to receive `NodeData<JsonData>`. | ||
| `CustomNodeDefinition` is intentionally **not** generic on the data type — its two existing generics (for `componentProps` and wrapper props) are unchanged, and custom-node `condition` / `component` continue to receive `NodeData<JsonData>`. |
Owner
Author
There was a problem hiding this comment.
Why is CustomNodeDefinition not generic?
Owner
Author
There was a problem hiding this comment.
Because customNodeDefinitions is a single array whose entries each match (via condition) differently-shaped nodes anywhere in the tree — a date string here, a {lat,lng} object there. One document-level T can't describe that heterogeneous set, and making CustomNodeDefinition generic would force every entry in the array to share a T, making mixed-shape definition arrays unusable. So it keeps only its two existing generics (componentProps / wrapper props), and condition / component receive NodeData<JsonData>. I've expanded the note in the guide to say this.
This was
linked to
issues
Jun 4, 2026
This was referenced Jun 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements roadmap §14 (terminology — "node, not component") + §15 (CustomNode flags audit) together. Closes #253, closes #254.
Final naming decided in the #253/#254 comment threads. The organizing principle: a node is a position in the data tree; a component is the React function that renders it.
Renames (breaking, v2)
element→component,customKey→keyComponent,wrapperElement→wrapperComponentcustomNodeProps→componentProps(feedscomponent+keyComponent)hideKey→showKey(polarity inverted, defaulttrue),showInTypesSelector→showInTypeSelectorCustomNodeProps→CustomComponentProps(also resolves theCustomNodeProps/CustomNodeDefinitionname clash)CustomWrapperPropstypeswrapperComponent, which now receives its config aswrapperProps(previously delivered ascustomNodeProps) — the only genuine set≠read asymmetry.CustomNodeDataFC fields gained theCustomprefix (CustomComponent/CustomWrapperComponent/CustomKeyComponent).CustomNodeDefinitionandCustomKeyPropskeep their names. No fields removed;passOriginalNodestays opt-in (perf),showOnView:true/showOnEdit:falseasymmetry kept — both now documented intypes.ts.Scope
src/): types,getCustomNode,CollectionNode,ValueNodeWrapper,KeyDisplay, exports.@json-edit-react/components: all 12 definitions + component bodies.demo/src/) andcustom-component-library.demo/src/v1/left on published v1.CustomWrapperPropsadded to the exported-types list, migration-guide §13 with before/after, componentsCLAUDE.md.major/majorfor the rename; two existing changesets aligned tocomponentPropsso the v2 changelog stays coherent.Tests
New
test/customNode.test.tsx— 23 tests pinning the DOM/setDatacontract of everyCustomNodeDefinitionfield (slots, props pass-through incl. awrapperProps-delivery regression guard, the view/edit visibility matrix, chrome flags, type selector +defaultValue,passOriginalNode, collection flags, and both JSON hooks).These caught a real regression: the
hideKey→showKeypolarity flip hid keys on non-custom nodes (no matching definition →showKeyundefined → falsy), because the default only applied insidegetCustomNode. Fixed by defaultingshowKey = trueat the consumption sites (mirrors the existingshowEditToolspattern).Verification
pnpm compile(tsc + ts-prune) ✅ ·pnpm lint✅ · 337 tests pass (2 pre-existing todos) ✅ ·pnpm -r build(all 4 packages) ✅@json-edit-react→../src) ✅tscshows pre-existing@types/react-version-mismatch errors in core'sJsonEditor.tsx/JsonViewer.tsx(identical count with this branch stashed) — unrelated to this change.🤖 Generated with Claude Code