Components

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.

Client Only

Installation

pnpm dlx @docyrus/cli add @docyrus/ui-jsonata-editor
Required Packages(9 packages)
pnpm add jsonata @uiw/react-codemirror @uiw/codemirror-extensions-langs @codemirror/autocomplete @codemirror/language @codemirror/lint @codemirror/state @codemirror/view @lezer/highlight
UI Primitives(2 components)
npx shadcn@latest add button dropdown-menu

Overview

JsonataEditor is a self-contained workbench for the JSONata query and transformation language — modeled on the official JSONata Exerciser. It has three panes:

PaneRole
Input JSONA CodeMirror editor for the JSON document the expression runs against.
ExpressionA JSONata editor with syntax highlighting, autocomplete, hover docs and inline parse-error linting.
ResultA 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 $name inside 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 / input yourself or let the component own them.
  • Flexible layout — horizontal or vertical, 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 bindings object if it is dynamic — the editor reads it on each evaluation but does not re-evaluate when only bindings changes.

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

PropTypeDefaultDescription
expressionstring—Controlled JSONata expression.
defaultExpressionstring—Initial expression for uncontrolled usage.
onExpressionChange(expression: string) => void—Fired whenever the expression changes.
inputstring | unknown—Controlled JSON input — a string or any JSON-serializable value.
defaultInputstring | unknown—Initial JSON input for uncontrolled usage.
onInputChange(input: string) => void—Fired whenever the input text changes.
bindingsRecord<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.
samplesJsonataSample[]—Pre-defined input + expression pairs for the samples menu.
aiAssistantboolean | 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.
aiAssistantOpenboolean—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.
showInputbooleantrueShow the JSON input pane.
showResultbooleantrueShow the result pane.
showToolbarbooleantrueShow the header toolbar.
titlestring'JSONata'Title shown in the toolbar.
orientation'horizontal' | 'vertical''horizontal'Pane arrangement.
debounceMsnumber300Debounce before evaluating, in ms.
evaluationTimeoutnumber1000Evaluation timeout in ms. 0 disables the timebox.
readOnlybooleanfalseDisables editing of both panes.
heightnumber | string'28rem'Overall editor height.
placeholderstring—Placeholder shown in the empty expression pane.
classNamestring—Root element className.

JsonataCodeEditor

PropTypeDefaultDescription
valuestring—Current expression text.
onChange(value: string) => void—Fired on every edit.
readOnlybooleanfalseDisables editing.
placeholderstring—Placeholder shown when empty.
autoFocusbooleanfalseFocus the editor on mount.
minHeightstring'2.5rem'Minimum editor height.
maxHeightstring'12rem'Maximum editor height.
heightstring—Fixed editor height — overrides minHeight / maxHeight.
lineNumbersbooleanfalseShow line numbers.
lintbooleantrueEnable the parse-error linter.
basicSetupBasicSetupOptions—CodeMirror basicSetup overrides.
extensionsExtension[]—Extra CodeMirror extensions appended after the JSONata language.
classNamestring—Wrapper className.

useJsonata

useJsonata(expression: string, input: unknown, options?: UseJsonataOptions): JsonataEvaluationState
OptionTypeDefaultDescription
debounceMsnumber300Debounce before evaluating, in ms.
enabledbooleantrueWhen false, evaluation is paused.
bindingsRecord<string, unknown>—Variable bindings injected into evaluate().
timeoutnumber1000Evaluation timeout in ms. 0 disables the timebox.
maxDepthnumber500Maximum recursion depth before aborting.

Components

ComponentDescription
JsonataEditorThe three-pane input / expression / result workbench.
JsonataCodeEditorThe standalone JSONata expression editor.
useApplyJsonataHook 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

TypeDescription
JsonataEditorPropsProps for JsonataEditor.
JsonataCodeEditorPropsProps for JsonataCodeEditor.
JsonataEditorOrientation'horizontal' | 'vertical'.
JsonataSampleA { name, description?, input, expression } sample pair.
JsonataAIAssistantConfigConfig for the AI Assistant drawer — onSendMessage, suggestions, title, defaultOpen, width, placeholder, emptyStateDescription.
IJsonataAiAssistantRenderContextArgument passed to renderAiAssistant — { open, width, onClose, expression, input }.
IUseApplyJsonataResultReturn shape of useApplyJsonata — { expression, setExpression, input, setInput, tools }.
JsonataAIMessageContextContext passed to onSendMessage: current expression, input and history.
JsonataChatMessageA { id, role: 'user' | 'assistant', content } chat entry.
JsonataEvaluationStateDiscriminated union describing an evaluation outcome.
JsonataEvaluationErrorNormalized parse / evaluation error (phase, message, code?, position?, token?).
JsonataErrorPhase'input' | 'parse' | 'evaluate'.
JsonataEvaluateOptionsOptions for evaluateJsonata.
UseJsonataOptionsOptions for the useJsonata hook.
JsonataFunctionA built-in function descriptor used by IntelliSense.
JsonataFunctionParamA single parameter of a JsonataFunction.

Helpers

ExportDescription
useJsonataHeadless hook — debounced evaluation against a parsed input.
evaluateJsonataCompiles and runs an expression; never throws.
parseJsonInputParses the JSON input text into a value or an error.
stringifyResultPretty-prints an evaluation result for display.
jsonataExtensionsCodeMirror extension bundle (language, autocomplete, hover, linter).
jsonataLanguageCodeMirror LanguageSupport for JSONata.
JSONATA_FUNCTIONSThe built-in function catalog.
JSONATA_FUNCTION_MAPMap of function name → descriptor.
JSONATA_SAMPLESReady-made sample input + expression pairs.

On this page