Prompt Detail Page#
The prompt detail page in Phoenix is composed of two distinct but structurally similar page components — PromptIndexPage (latest version) and PromptVersionDetailsPage (a specific version) — both nested under a shared PromptLayout shell that provides the tab navigation and page header.
Component Hierarchy#
PromptLayout (tab nav, header, Clone/Edit buttons)
├── [Tab: "Prompt"] → PromptIndexPage
│ ├── PromptChatMessagesCard (chat template messages)
│ ├── PromptModelConfigurationCard
│ │ ├── PromptLLM
│ │ ├── PromptInvocationParameters
│ │ ├── PromptTools
│ │ └── PromptResponseFormat ← renders JSONBlock via CodeMirror
│ └── PromptCodeExportCard (Python / TypeScript SDK snippets)
│
├── [Tab: "Versions"] → PromptVersionsPage
│ └── /:versionId → PromptVersionDetailsPage
│ ├── PromptChatMessagesCard
│ ├── PromptModelConfigurationCard
│ └── PromptCodeExportCard
│
└── [Tab: "Config"] → PromptConfigPage
PromptIndexPage renders the latest version (first edge from promptVersions), while PromptVersionDetailsPage reads a specific version via promptVersionLoader . Both pages render the same three card components but with different data sources.
Routing & URL Mapping#
Routes are defined in app/src/Routes.tsx. The :promptId parent route mounts PromptLayout as a shared layout; nested routes drive the active tab :
| URL | Component |
|---|---|
/prompts/:promptId | PromptIndexPage |
/prompts/:promptId/versions | PromptVersionsPage |
/prompts/:promptId/versions/:versionId | PromptVersionDetailsPage |
/prompts/:promptId/config | PromptConfigPage |
The :promptId route has shouldRevalidate={() => true} to force revalidation after mutations.
Tab Navigation & Scroll Container Architecture#
PromptLayout implements a flex-column layout with overflow: hidden propagated down through the tab pane container, ensuring that each tab panel fills the remaining viewport height without causing the outer shell to scroll .
The critical CSS applied to .ac-tabs and its children :
.ac-tabs:flex: 1 1 auto; display: flex; flex-direction: column; overflow: hidden.ac-tabs__pane-container:flex: 1 1 auto; overflow: hiddendiv[role="tabpanel"]:not([hidden]):flex: 1 1 auto; overflow: hidden
This means each tab panel owns its own scroll container. In PromptIndexPage, the inner View with overflow="auto" and data-testid="scroll-container" is the actual scrollable element . In PromptVersionDetailsPage, the outermost View carries overflow="auto" . Content is max-width constrained to 900px and centered via marginStart/End="auto" in both pages .
Tab selection is URL-driven: onSelectionChange calls navigate() to update the URL , and the initial selected tab is derived from pathname on mount .
Card Components#
All three card components accept a Relay fragment key (promptVersion) and use useFragment for type-safe, fine-grained data fetching.
PromptChatMessagesCard — collapsible Card rendering the chat template. Supports both PromptChatTemplate and PromptStringTemplate variants. Individual messages are rendered as ChatTemplateMessageCard with typed content parts (text, tool_call, tool_result).
PromptModelConfigurationCard — collapsible Card with a DisclosureGroup (default expanded: llm, invocation-parameters, tools, response-format). Orchestrates four sub-components. PromptResponseFormat sits at the bottom of this group and renders JSON via JSONBlock.
PromptCodeExportCard — adds local language state ("Python" | "TypeScript") toggled by a CodeLanguageRadioGroup. Generates code snippets with mapPromptToSDKSnippet / mapPromptToClientSnippet, presented in two disclosure sections ("SDK Inline" and "Using the Client"), each with a CopyToClipboardButton.
JSON Rendering via CodeMirror (JSONBlock)#
JSONBlock is a thin, read-only CodeMirror wrapper at app/src/components/code/JSONBlock.tsx. Key characteristics:
- Extensions:
json()(syntax),EditorView.lineWrapping,linter(jsonParseLinter())— live parse error underlines - Read-only:
editable={false}always - Theme:
githubLight/githubDarkfrom@uiw/codemirror-theme-github, toggled viauseTheme() - Default setup: line numbers, fold gutter, bracket matching, syntax highlighting; active-line highlight is disabled
- Extensible: accepts
basicSetupoverrides via props
In PromptResponseFormat, the response format definition (a raw JSON string or object) is normalized via safelyParseJSON / safelyStringifyJSON before being passed to JSONBlock. If no response format is defined, a disclosure panel with an empty-state message is shown instead .