Lean 4’s infoview is not a static display. It is a React application that can render arbitrary user-defined components, call server-side Lean functions over an RPC channel, and display rich interactive data including goal states, diagnostics, and custom visualizations. This page explains how widgets are defined and registered, how the RPC protocol connects the client to the Lean server, and how the language server itself is structured.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/leanprover/lean4/llms.txt
Use this file to discover all available pages before exploring further.
User Widgets
What a widget is
A user widget is a JavaScript ES module that exports a React component. The infoview loads it and renders it whenever the text cursor is within the syntactic range of a widget invocation. Widgets can call back into the Lean server using the RPC protocol to fetch live data: current goal state, elaboration information, or the result of any function you mark with@[server_rpc_method].
Declaring a widget module
Define the JavaScript (or TypeScript compiled to JS) as a LeanString and wrap it in the Lean.Widget.Module structure, then tag the constant with @[widget_module]:
Module structure (from Lean.Widget.Types) holds the JavaScript source and caches its hash:
The JavaScript environment inside the infoview provides
@leanprover/infoview and react as importable modules. You access them via bare import specifiers rather than URLs.Using include_str for external JS files
For larger widgets you typically maintain the JavaScript in a separate .js file and embed it with include_str:
Attaching a widget to a position with savePanelWidgetInfo
To display a widget at a specific location in a file, call Widget.savePanelWidgetInfo from within a command elaborator or tactic. The function records a WidgetInstance in the InfoTree at the current syntax position:
hash must equal (ToModule.toModule c).javascriptHash for some @[widget_module]-tagged constant c. A typical elaborator pattern:
Persistent panel widgets with show_panel_widgets
The show_panel_widgets command registers a widget to appear globally (or in a scoped/local region) without any per-invocation elaboration:
The deprecated UserWidgetDefinition
Older code used Widget.UserWidgetDefinition with @[widget]. This form is still supported for backward compatibility:
@[widget_module] with Module for new code.
The RPC Protocol
Architecture overview
The infoview communicates with the Lean server through the LSP$/lean/rpc/call request. The client maintains an RPC session per open file. Each call carries a method name (a Lean Name) and a JSON-encoded parameter object. The server dispatches the call to a registered RpcProcedure.
Registering a server-side RPC method
Annotate any function of typeα → RequestM (RequestTask β) with @[server_rpc_method], where both α and β implement Server.RpcEncodable:
@[server_rpc_method] attribute is defined in Lean.Server.Rpc.RequestHandling:
RpcEncodable and WithRpcRef
Server.RpcEncodable is the typeclass for types that can be serialized over the RPC channel. It extends JSON encoding with support for WithRpcRef, which lets you pass server-side object references (closures, environment snapshots, etc.) to the client as opaque handles that the client returns on the next call.
Key Widget Data Types
Lean.Widget.TaggedText
TaggedText is the core display type for pretty-printed terms and goals. It is a rose tree of text fragments where leaves carry semantic tags that the infoview uses to render interactive popups and hyperlinks.
Lean.Widget.InteractiveGoal
An InteractiveGoal wraps a tactic-mode goal for display in the infoview. It embeds CodeWithInfos (a TaggedText specialization) so every subterm is clickable:
Lean.Widget.getInteractiveGoals returns the current goals at a given position, and the infoview calls it whenever the cursor moves.
Lean.Widget.InteractiveTermGoal
The term-mode counterpart embeds the range of the term as well as the TermInfo from elaboration:
Lean.Widget.InteractiveCode (CodeWithInfos)
CodeWithInfos is TaggedText SubexprInfo, where SubexprInfo identifies a subexpression in the elaborated term. The infoview uses it to show hover popups and “go to definition” links directly in the goal display.
Language Server Architecture
The watchdog / file-worker split
The Lean language server (lean --server) is implemented in Lean.Server and follows a strict two-process architecture described in src/Lean/Server/README.md:
InfoTree, answers hover/completion/go-to-definition requests, and generates diagnostics. If a worker crashes (e.g., due to a stack overflow in user metaprogram code), only that file is affected; the watchdog restarts it while all other open files continue operating.
Snapshots and incrementality
Workers use the snapshot system (Lean.Language.Snapshot) to make file processing incremental. A snapshot captures the elaboration state after each top-level command. When the user edits the file, the worker reuses any snapshot whose preceding commands have not changed, re-elaborating only from the first affected command onward.
Request handlers locate the relevant snapshot using withWaitFindSnap, which asynchronously waits for elaboration to reach the cursor position before responding.
LSP capabilities implemented
The Lean server implements the following standard LSP features:Document synchronization
Document synchronization
textDocument/didOpen,didChange,didClose,didSaveworkspace/didChangeWatchedFiles(.leanand.ileanfiles)$/lean/fileProgress— incremental elaboration progress notifications
Navigation
Navigation
Editing assistance
Editing assistance
textDocument/completion,completionItem/resolvetextDocument/hovertextDocument/signatureHelptextDocument/codeAction,codeAction/resolvetextDocument/documentHighlighttextDocument/semanticTokens/full,textDocument/semanticTokens/rangetextDocument/inlayHinttextDocument/foldingRange,textDocument/documentSymbol
Lean-specific RPC methods
Lean-specific RPC methods
Lean.Widget.getInteractiveDiagnosticsLean.Widget.getInteractiveGoalsLean.Widget.getInteractiveTermGoalLean.Widget.getWidgetsLean.Widget.getWidgetSourceLean.Widget.getGoToLocationLean.Widget.lazyTraceChildrenToInteractiveLean.Widget.highlightMatchesLean.Widget.InteractiveDiagnostics.infoToInteractiveLean.Widget.InteractiveDiagnostics.msgToInteractive