useInterrupt

React hook for handling agent interrupt events and resuming execution with user input


Overview

useInterrupt listens for agent custom events named on_interrupt, captures the latest interrupt payload for a run, and renders interrupt UI once the run finalizes. Your UI can call resolve(response) to resume the agent with a resume payload.

Re-exported from @copilotkit/react-core/v2. It is identical to the React (V2) useInterrupt; only the import path differs.

By default, interrupt UI is rendered inside CopilotChat automatically. If you set renderInChat: false, the hook returns the element so you can place it manually. The element returned by render is a React Native element.

event.value is typed as any since the interrupt payload shape depends on your agent. Type-narrow it in your callbacks (e.g. handler, enabled, render) as needed.

Signature

import { useInterrupt } from "@copilotkit/react-native";

function useInterrupt<
  TResult = never,
  TRenderInChat extends boolean | undefined = undefined,
>(
  config: UseInterruptConfig<any, TResult, TRenderInChat>,
): TRenderInChat extends false
  ? React.ReactElement | null
  : TRenderInChat extends true | undefined
    ? void
    : React.ReactElement | null | void;

Parameters

Prop

Type

Return Value

Prop

Type

Usage

In-chat interrupt UI (default)

import { Text, TouchableOpacity, View } from "react-native";
import { useInterrupt } from "@copilotkit/react-native";

function ApprovalInterrupt() {
  useInterrupt({
    render: ({ event, resolve }) => (
      <View style={{ padding: 12, borderWidth: 1, borderRadius: 8 }}>
        <Text>{event.value.question}</Text>
        <View style={{ marginTop: 8, flexDirection: "row", gap: 8 }}>
          <TouchableOpacity onPress={() => resolve({ approved: true })}>
            <Text>Approve</Text>
          </TouchableOpacity>
          <TouchableOpacity onPress={() => resolve({ approved: false })}>
            <Text>Reject</Text>
          </TouchableOpacity>
        </View>
      </View>
    ),
  });

  return null;
}

Manual placement with async preprocessing

import { Text, TouchableOpacity, View } from "react-native";
import { useInterrupt } from "@copilotkit/react-native";

function SidePanelInterrupt() {
  const element = useInterrupt({
    renderInChat: false,
    enabled: (event) => event.value.startsWith("approval:"),
    handler: async ({ event }) => ({ label: event.value.toUpperCase() }),
    render: ({ event, result, resolve }) => (
      <View style={{ borderWidth: 1, borderRadius: 8, padding: 12 }}>
        <Text style={{ fontWeight: "500" }}>{result?.label ?? ""}</Text>
        <Text style={{ marginTop: 8 }}>{event.value}</Text>
        <TouchableOpacity style={{ marginTop: 8 }} onPress={() => resolve({ accepted: true })}>
          <Text>Continue</Text>
        </TouchableOpacity>
      </View>
    ),
  });

  return <>{element}</>;
}

Behavior

  • Interrupts are collected from agent custom events named on_interrupt.
  • Interrupt UI is surfaced when the run finalizes.
  • Starting a new run clears pending interrupt state.
  • event.value is any; type-narrow in your callbacks as needed.
  • render.result is inferred from handler return type and is always TResult | null.
  • If handler throws or rejects, result is set to null.

Related