showcase/shell-docs/src/content/reference/react-native/hooks/useInterrupt.mdx
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.
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.
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;
<PropertyReference name="handler" type="(props: InterruptHandlerProps) => TResult | PromiseLike<TResult>"
Optional preprocessing callback. Runs before rendering and can return sync or
async data that is exposed as result in render. TResult is automatically
inferred from the handler's return type. If the handler throws/rejects,
result is null.
</PropertyReference>
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;
}
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}</>;
}
on_interrupt.event.value is any; type-narrow in your callbacks as needed.render.result is inferred from handler return type and is always TResult | null.handler throws or rejects, result is set to null.useHumanInTheLoop: structured interactive tool workflowsuseFrontendTool: client-side tool registrationuseAgent: access and subscribe to agent events