Jsonata Editor
A three-pane workbench for writing and evaluating JSONata expressions — a JSON input pane, an expression editor with IntelliSense, and a live result pane.
Installation
pnpm dlx @docyrus/cli add @docyrus/ui-jsonata-editorpnpm add jsonata @uiw/react-codemirror @uiw/codemirror-extensions-langs @codemirror/autocomplete @codemirror/language @codemirror/lint @codemirror/state @codemirror/view @lezer/highlightnpx shadcn@latest add button dropdown-menuOverview
JsonataEditor is a self-contained workbench for the
JSONata query and transformation language — modeled on
the official JSONata Exerciser. It has three panes:
| Pane | Role |
|---|---|
| Input JSON | A CodeMirror editor for the JSON document the expression runs against. |
| Expression | A JSONata editor with syntax highlighting, autocomplete, hover docs and inline parse-error linting. |
| Result | A read-only, live-updating view of the evaluated result (or the error). |
Evaluation happens automatically as you type (debounced). Expressions are guarded by a timebox so an infinite loop or runaway recursion can't lock the page.
Features
- Live evaluation — debounced re-evaluation on every input or expression change.
- IntelliSense — autocomplete with snippet expansion for ~60 built-in
$functions, grouped by category. - Hover documentation — signatures, parameter docs and examples on hover.
- Inline linting — parse errors are underlined in the expression editor.
- Timeboxed — runaway recursion and infinite loops are aborted (
evaluationTimeout). - Variable bindings — inject values accessible as
$nameinside the expression. - Samples menu — load ready-made input + expression pairs.
- AI Assistant drawer — opt-in chat panel built on ai-elements that slides in from the left and wires to your own LLM via
onSendMessage. - Controlled or uncontrolled — drive
expression/inputyourself or let the component own them. - Flexible layout —
horizontalorvertical, with the input and result panes individually toggleable. - Theme-aware — follows the Docyrus light / dark theme.
Usage
import { JsonataEditor } from "@docyrus/ui/components/jsonata-editor";
export function Example() {
return (
<JsonataEditor
defaultExpression="$sum(Account.Order.Product.(Price * Quantity))"
defaultInput={{ Account: { Order: [] } }}
onResult={(result) => console.log(result)}
/>
);
}Controlled
Drive both panes from your own state:
const [expression, setExpression] = useState("");
const [input, setInput] = useState("{}");
<JsonataEditor
expression={expression}
onExpressionChange={setExpression}
input={input}
onInputChange={setInput}
/>Variable bindings
Values passed via bindings are available inside the expression as $name:
<JsonataEditor
defaultExpression="$greeting & ', ' & name"
defaultInput={{ name: "Ada" }}
bindings={{ greeting: "Hello" }}
/>Memoize the
bindingsobject if it is dynamic — the editor reads it on each evaluation but does not re-evaluate when onlybindingschanges.
AI Assistant
Pass an aiAssistant config to add an AI Assistant button to the toolbar.
Clicking it slides a chat drawer in from the left of the editor, built on
ai-elements (Conversation, Message). Wire onSendMessage to your LLM and
return the reply (sync or async):
<JsonataEditor
defaultExpression="$sum(items.price)"
aiAssistant={{
onSendMessage: async (message, { expression, input, history }) => {
const response = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ message, expression, input, history })
});
const { reply } = await response.json();
return reply;
},
suggestions: [
"Explain this expression",
"Show me a $map example",
"How do I filter an array?"
]
}}
/>Pass aiAssistant={true} to mount the chat shell without a backend (useful
while wiring one — messages are stored locally but no reply arrives).
Standalone expression editor
JsonataCodeEditor is the expression pane on its own — drop it into a form or
an automation node config when you only need to capture a JSONata expression.
import { JsonataCodeEditor } from "@docyrus/ui/components/jsonata-editor";
const [value, setValue] = useState("");
<JsonataCodeEditor
value={value}
onChange={setValue}
placeholder="Enter a JSONata expression"
/>Headless evaluation
useJsonata debounces and evaluates an expression against an already-parsed
input value, with no UI:
import { useJsonata } from "@docyrus/ui/components/jsonata-editor";
function useTotal(order: unknown) {
const state = useJsonata("$sum(items.price)", order);
return state.status === "success" ? state.result : undefined;
}For a one-off evaluation outside React, use evaluateJsonata:
import { evaluateJsonata } from "@docyrus/ui/components/jsonata-editor";
const state = await evaluateJsonata("$count(items)", { items: [1, 2, 3] });
// → { status: "success", result: 3 }EditorAgent integration
Pair useApplyJsonata with renderAiAssistant to give an LLM a writable expression pane. The hook owns the editor's controlled state and ships a applyJsonata tool that the agent calls to commit a new expression — the tool also evaluates against the current input JSON and returns a result preview so the agent can self-correct.
import { EditorAgent } from '@docyrus/ui/components/editor-agent';
import { JsonataEditor, useApplyJsonata } from '@docyrus/ui/components/jsonata-editor';
export function JsonataPlayground({ client, user, agentId }) {
const jsonata = useApplyJsonata();
const [aiOpen, setAiOpen] = useState(false);
return (
<JsonataEditor
expression={jsonata.expression}
onExpressionChange={jsonata.setExpression}
input={jsonata.input}
onInputChange={jsonata.setInput}
aiAssistantOpen={aiOpen}
onAiAssistantOpenChange={setAiOpen}
renderAiAssistant={({ open, onClose }) => (
<EditorAgent
agentId={agentId}
client={client}
user={user}
open={open}
onClose={onClose}
clientTools={jsonata.tools}
/>
)}
/>
);
}The matching backend agent must register a tool named applyJsonata whose input schema mirrors { expression: string (required), input?: any, explanation?: string }.
API Reference
JsonataEditor
| Prop | Type | Default | Description |
|---|---|---|---|
expression | string | — | Controlled JSONata expression. |
defaultExpression | string | — | Initial expression for uncontrolled usage. |
onExpressionChange | (expression: string) => void | — | Fired whenever the expression changes. |
input | string | unknown | — | Controlled JSON input — a string or any JSON-serializable value. |
defaultInput | string | unknown | — | Initial JSON input for uncontrolled usage. |
onInputChange | (input: string) => void | — | Fired whenever the input text changes. |
bindings | Record<string, unknown> | — | Variable bindings injected into evaluate() (accessible as $name). |
onResult | (result: unknown) => void | — | Fired after a successful evaluation. |
onError | (error: JsonataEvaluationError) => void | — | Fired when parsing the input or running the expression fails. |
onEvaluate | (state: JsonataEvaluationState) => void | — | Fired after every evaluation, regardless of outcome. |
samples | JsonataSample[] | — | Pre-defined input + expression pairs for the samples menu. |
aiAssistant | boolean | JsonataAIAssistantConfig | — | When provided, mounts the built-in AI Assistant drawer on the left. Pass true for the UI shell only, or a config object with onSendMessage to wire a backend. |
aiAssistantOpen | boolean | — | Controlled open state for the AI Assistant drawer. When provided the editor stops managing the open state internally — pair with onAiAssistantOpenChange. |
onAiAssistantOpenChange | (open: boolean) => void | — | Fired when the AI Assistant toolbar button toggles the drawer. |
renderAiAssistant | (ctx: IJsonataAiAssistantRenderContext) => ReactNode | — | Replaces the built-in drawer body. When set, the toolbar button is shown even if aiAssistant is omitted, the drawer animates in as usual, and this render fn supplies the body. Use this to mount a custom agent (e.g. <EditorAgent>) inside the drawer. |
showInput | boolean | true | Show the JSON input pane. |
showResult | boolean | true | Show the result pane. |
showToolbar | boolean | true | Show the header toolbar. |
title | string | 'JSONata' | Title shown in the toolbar. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Pane arrangement. |
debounceMs | number | 300 | Debounce before evaluating, in ms. |
evaluationTimeout | number | 1000 | Evaluation timeout in ms. 0 disables the timebox. |
readOnly | boolean | false | Disables editing of both panes. |
height | number | string | '28rem' | Overall editor height. |
placeholder | string | — | Placeholder shown in the empty expression pane. |
className | string | — | Root element className. |
JsonataCodeEditor
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Current expression text. |
onChange | (value: string) => void | — | Fired on every edit. |
readOnly | boolean | false | Disables editing. |
placeholder | string | — | Placeholder shown when empty. |
autoFocus | boolean | false | Focus the editor on mount. |
minHeight | string | '2.5rem' | Minimum editor height. |
maxHeight | string | '12rem' | Maximum editor height. |
height | string | — | Fixed editor height — overrides minHeight / maxHeight. |
lineNumbers | boolean | false | Show line numbers. |
lint | boolean | true | Enable the parse-error linter. |
basicSetup | BasicSetupOptions | — | CodeMirror basicSetup overrides. |
extensions | Extension[] | — | Extra CodeMirror extensions appended after the JSONata language. |
className | string | — | Wrapper className. |
useJsonata
useJsonata(expression: string, input: unknown, options?: UseJsonataOptions): JsonataEvaluationState| Option | Type | Default | Description |
|---|---|---|---|
debounceMs | number | 300 | Debounce before evaluating, in ms. |
enabled | boolean | true | When false, evaluation is paused. |
bindings | Record<string, unknown> | — | Variable bindings injected into evaluate(). |
timeout | number | 1000 | Evaluation timeout in ms. 0 disables the timebox. |
maxDepth | number | 500 | Maximum recursion depth before aborting. |
Components
| Component | Description |
|---|---|
JsonataEditor | The three-pane input / expression / result workbench. |
JsonataCodeEditor | The standalone JSONata expression editor. |
useApplyJsonata | Hook that owns expression + input state and exposes the applyJsonata client-side tool for <EditorAgent> — the tool writes the agent's expression AND evaluates it, returning a result preview or a categorized error (input-empty / parse / evaluate). See EditorAgent integration below. |
Type Exports
| Type | Description |
|---|---|
JsonataEditorProps | Props for JsonataEditor. |
JsonataCodeEditorProps | Props for JsonataCodeEditor. |
JsonataEditorOrientation | 'horizontal' | 'vertical'. |
JsonataSample | A { name, description?, input, expression } sample pair. |
JsonataAIAssistantConfig | Config for the AI Assistant drawer — onSendMessage, suggestions, title, defaultOpen, width, placeholder, emptyStateDescription. |
IJsonataAiAssistantRenderContext | Argument passed to renderAiAssistant — { open, width, onClose, expression, input }. |
IUseApplyJsonataResult | Return shape of useApplyJsonata — { expression, setExpression, input, setInput, tools }. |
JsonataAIMessageContext | Context passed to onSendMessage: current expression, input and history. |
JsonataChatMessage | A { id, role: 'user' | 'assistant', content } chat entry. |
JsonataEvaluationState | Discriminated union describing an evaluation outcome. |
JsonataEvaluationError | Normalized parse / evaluation error (phase, message, code?, position?, token?). |
JsonataErrorPhase | 'input' | 'parse' | 'evaluate'. |
JsonataEvaluateOptions | Options for evaluateJsonata. |
UseJsonataOptions | Options for the useJsonata hook. |
JsonataFunction | A built-in function descriptor used by IntelliSense. |
JsonataFunctionParam | A single parameter of a JsonataFunction. |
Helpers
| Export | Description |
|---|---|
useJsonata | Headless hook — debounced evaluation against a parsed input. |
evaluateJsonata | Compiles and runs an expression; never throws. |
parseJsonInput | Parses the JSON input text into a value or an error. |
stringifyResult | Pretty-prints an evaluation result for display. |
jsonataExtensions | CodeMirror extension bundle (language, autocomplete, hover, linter). |
jsonataLanguage | CodeMirror LanguageSupport for JSONata. |
JSONATA_FUNCTIONS | The built-in function catalog. |
JSONATA_FUNCTION_MAP | Map of function name → descriptor. |
JSONATA_SAMPLES | Ready-made sample input + expression pairs. |