Studio Notebook

Claude Code Atlas

Local Jsx Commands And Ui Handoffs

Learn the UI-heavy path where a command opens a screen or hands control to React.

Why this matters

local-jsx is the command kind for opening a screen or performing a UI-aware state handoff. It is the branch you use when plain text is not enough, and the command needs to show React UI before control returns to the session.

Unlike a text-only result, a local-jsx command needs onDone because the UI has to tell the runtime when it is finished, what should happen next, and whether any follow-up input should be sent. That is why these commands get richer context instead of returning a plain text result.

Three representative cases

The three representative cases are /help, /context, and /plan.

/help is the smallest lazy UI command. It loads its implementation only when the command is invoked, then renders a help screen and hands control back through onDone.

import type { Command } from '../../commands.js'

const help = {
  type: 'local-jsx',
  name: 'help',
  description: 'Show help and available commands',
  load: () => import('./help.js'),
} satisfies Command

export default help
import * as React from 'react';
import { HelpV2 } from '../../components/HelpV2/HelpV2.js';
import type { LocalJSXCommandCall } from '../../types/command.js';
export const call: LocalJSXCommandCall = async (onDone, {
  options: {
    commands
  }
}) => {
  return <HelpV2 commands={commands} onClose={onDone} />;
};

/context shows the interactive and non-interactive split. The interactive command is a local-jsx view, while the non-interactive version is a separate local command that stays available when there is no live UI.

import { getIsNonInteractiveSession } from '../../bootstrap/state.js'
import type { Command } from '../../commands.js'

export const context: Command = {
  name: 'context',
  description: 'Visualize current context usage as a colored grid',
  isEnabled: () => !getIsNonInteractiveSession(),
  type: 'local-jsx',
  load: () => import('./context.js'),
}
export const contextNonInteractive: Command = {
  type: 'local',
  name: 'context',
  supportsNonInteractive: true,
  description: 'Show current context usage',
  get isHidden() {
    return !getIsNonInteractiveSession()
  },
  isEnabled() {
    return getIsNonInteractiveSession()
  },
  load: () => import('./context-noninteractive.js'),
}

/plan is the command that changes session mode and can also render the existing plan content once the mode switch is done. Its loader advertises the argument hint up front, and its implementation shows the mode transition path before it falls through to rendering the saved plan.

import type { Command } from '../../commands.js'

const plan = {
  type: 'local-jsx',
  name: 'plan',
  description: 'Enable plan mode or view the current session plan',
  argumentHint: '[open|<description>]',
  load: () => import('./plan.js'),
} satisfies Command

export default plan
  if (currentMode !== 'plan') {
    handlePlanModeTransition(currentMode, 'plan');
    setAppState(prev => ({
      ...prev,
      toolPermissionContext: applyPermissionUpdate(prepareContextForPlanMode(prev.toolPermissionContext), {
        type: 'setMode',
        mode: 'plan',
        destination: 'session'
      })
    }));
    const description = args.trim();
    if (description && description !== 'open') {
      onDone('Enabled plan mode', {
        shouldQuery: true
      });
    } else {
      onDone('Enabled plan mode');
    }
    return null;
  }
  const editor = getExternalEditor();
  const editorName = editor ? toIDEDisplayName(editor) : undefined;
  const display = <PlanDisplay planContent={planContent} planPath={planPath} editorName={editorName} />;

  // Render to string and pass to onDone like local commands do
  const output = await renderToString(display);
  onDone(output);
  return null;

Why immediate shows up here

Some UI-facing commands need to run right away instead of waiting in the queue. The mcp command is a concrete example of that rule.

import type { Command } from '../../commands.js'

const mcp = {
  type: 'local-jsx',
  name: 'mcp',
  description: 'Manage MCP servers',
  immediate: true,
  argumentHint: '[enable|disable [server-name]]',
  load: () => import('./mcp.js'),
} satisfies Command

export default mcp

The main pattern is simple: local-jsx commands are for screens and handoffs, not plain text. They start with a lazy loader, render a UI, and use onDone to return control to the runtime when the UI work is finished.