Implementation:Langgenius Dify Modal Context
| Knowledge Sources | |
|---|---|
| Domains | Frontend, UI State Management, Modal Management |
| Last Updated | 2026-02-12 07:00 GMT |
Overview
A centralized React context provider that manages the lifecycle of all application-level modal dialogs in the Dify frontend.
Description
The ModalContext is a comprehensive modal management system implemented as a React context using the use-context-selector library. It provides a single point of control for showing, hiding, and interacting with over a dozen distinct modal types throughout the Dify application, including account settings, pricing, model configuration, API-based extensions, moderation settings, external data tools, opening statement configuration, plugin updates, education notices, and trigger events limit modals.
The context stores ModalState objects for each modal type, where each state includes a typed payload along with optional callback functions for cancel, save, remove, edit, and validation-before-save operations. This pattern decouples the triggering of modals from their rendering location, allowing any component in the tree to open a modal without importing the modal component directly.
The ModalContextProvider component uses Next.js dynamic imports with ssr: false for all modal components, ensuring they are only loaded client-side when actually needed. Some modal states (account settings, pricing) are synchronized with URL query parameters via nuqs hooks, enabling deep-linking to specific modal views. The provider wires up all open/close/save/cancel handlers and renders the conditionally-visible modal components as siblings of the wrapped children.
Usage
Use useModalContext() or useModalContextSelector() from any component within the provider tree to trigger any application-level modal. This is the preferred mechanism for opening modals that need to be accessible from many different parts of the UI without prop drilling. The provider should be placed high in the component tree, typically wrapping the main application layout.
Code Reference
Source Location
- Repository: Langgenius_Dify
- File: web/context/modal-context.tsx
- Lines: 1-486
Signature
export type ModalState<T> = {
payload: T
onCancelCallback?: () => void
onSaveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onRemoveCallback?: (newPayload?: T, formValues?: Record<string, any>) => void
onEditCallback?: (newPayload: T) => void
onValidateBeforeSaveCallback?: (newPayload: T) => boolean
isEditMode?: boolean
datasetBindings?: { id: string, name: string }[]
}
export type ModelModalType = {
currentProvider: ModelProvider
currentConfigurationMethod: ConfigurationMethodEnum
currentCustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields
isModelCredential?: boolean
credential?: Credential
model?: CustomModel
mode?: ModelModalModeEnum
}
export type ModalContextState = {
setShowAccountSettingModal: Dispatch<SetStateAction<ModalState<AccountSettingTab> | null>>
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
setShowPricingModal: () => void
setShowAnnotationFullModal: () => void
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
setShowExternalKnowledgeAPIModal: Dispatch<SetStateAction<ModalState<CreateExternalAPIReq> | null>>
setShowModelLoadBalancingModal: Dispatch<SetStateAction<ModelLoadBalancingModalProps | null>>
setShowOpeningModal: Dispatch<SetStateAction<ModalState<OpeningStatement & { ... }> | null>>
setShowUpdatePluginModal: Dispatch<SetStateAction<ModalState<UpdatePluginPayload> | null>>
setShowEducationExpireNoticeModal: Dispatch<SetStateAction<ModalState<ExpireNoticeModalPayloadProps> | null>>
setShowTriggerEventsLimitModal: Dispatch<SetStateAction<ModalState<TriggerEventsLimitModalPayload> | null>>
}
export const useModalContext: () => ModalContextState
export const useModalContextSelector: <T,>(selector: (state: ModalContextState) => T) => T
export const ModalContextProvider: FC<{ children: React.ReactNode }>
Import
import { useModalContext, useModalContextSelector, ModalContextProvider } from '@/context/modal-context'
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| children | React.ReactNode |
Yes | Child components that will have access to the modal context |
Outputs
| Name | Type | Description |
|---|---|---|
| setShowAccountSettingModal | null>> | Opens or closes the account settings modal with a specific tab |
| setShowApiBasedExtensionModal | null>> | Opens or closes the API-based extension configuration modal |
| setShowModerationSettingModal | null>> | Opens or closes the moderation setting modal |
| setShowExternalDataToolModal | null>> | Opens or closes the external data tool modal |
| setShowPricingModal | () => void |
Opens the pricing/billing modal |
| setShowAnnotationFullModal | () => void |
Opens the annotation full modal |
| setShowModelModal | null>> | Opens or closes the model configuration modal |
| setShowExternalKnowledgeAPIModal | null>> | Opens or closes the external knowledge API modal |
| setShowModelLoadBalancingModal | null>> | Opens or closes the model load balancing modal |
| setShowOpeningModal | null>> | Opens or closes the conversation opening statement modal |
| setShowUpdatePluginModal | null>> | Opens or closes the plugin update modal |
| setShowEducationExpireNoticeModal | null>> | Opens or closes the education expiration notice modal |
| setShowTriggerEventsLimitModal | null>> | Opens or closes the trigger events limit modal |
Usage Examples
Opening the Account Settings Modal
import { useModalContext } from '@/context/modal-context'
function MyComponent() {
const { setShowAccountSettingModal } = useModalContext()
const handleOpenSettings = () => {
setShowAccountSettingModal({
payload: 'members',
onCancelCallback: () => console.log('Settings closed'),
})
}
return <button onClick={handleOpenSettings}>Open Settings</button>
}
Opening the Pricing Modal
import { useModalContextSelector } from '@/context/modal-context'
function UpgradeButton() {
const setShowPricingModal = useModalContextSelector(s => s.setShowPricingModal)
return <button onClick={setShowPricingModal}>Upgrade Plan</button>
}
Opening the Model Configuration Modal
import { useModalContext } from '@/context/modal-context'
function ModelConfig({ provider, configMethod }) {
const { setShowModelModal } = useModalContext()
const handleConfigure = () => {
setShowModelModal({
payload: {
currentProvider: provider,
currentConfigurationMethod: configMethod,
},
onSaveCallback: (payload, formValues) => {
console.log('Model saved:', payload, formValues)
},
})
}
return <button onClick={handleConfigure}>Configure Model</button>
}