Ant Design Modal focus restoration lacks a stable configuration contract
0 reputation · 18 May 2024, 00:57 UTC
0 reputation · 18 May 2024, 00:57 UTC
Ant Design's Modal component delegates focus containment to rc-dialog, but the library does not expose a stable, documented configuration prop that guarantees focus returns to the invoking element after dismissal. In user-facing workflows, this leaves keyboard and screen-reader users without a predictable return path when a modal closes via Escape, mask click, or action button. The behavior may vary between Ant Design v4 and v5, and developers often resort to custom afterClose or onCancel handlers to manually restore focus, creating inconsistent accessibility patterns across applications.
Nested modals, dynamically rendered triggers, and portaled containers further complicate the expected focus order, making it difficult to audit whether the return path is preserved. Without a library-wide contract, automated accessibility checks and manual screen-reader testing must account for implementation-specific handling rather than a single component guarantee.
Is there a documented, version-stable configuration in Ant Design v4 or v5 that enforces focus restoration to the trigger element after Modal dismissal? If no dedicated prop exists, what is the supported boundary for relying on rc-dialog's internal behavior across nested or portaled modal stacks? How should teams verify focus return behavior when custom afterClose handlers are already managing application state?
35275 reputation · 18 May 2024, 07:59 UTC
Ant Design v4 does not expose any prop that guarantees focus returns to the element that opened the Modal. In v5 a focusTriggerAfterClose prop exists, but it is not documented as a stable, public contract and may change in future releases. Therefore, for a version‑stable guarantee you must either rely on this prop in v5 (with caution) or implement explicit focus restoration in afterClose or onCancel callbacks.
The Modal component is a thin wrapper around rc‑dialog. rc‑dialog has an internal flag focusTriggerAfterClose that, when true, attempts to focus the element that triggered the dialog when the dialog’s afterClose lifecycle fires. In v4 the flag is always true by default, but the API surface does not expose it, so developers cannot guarantee its behaviour across releases. In v5 the flag can be set via the focusTriggerAfterClose prop, but the Ant Design docs do not list it as a stable contract; it is effectively an implementation detail.
When a modal is nested inside another modal, or when the modal’s portal container is moved outside the normal DOM hierarchy (for example, getContainer={false} or a custom getContainer function), the natural focus order can be disrupted. In these scenarios the internal focusTriggerAfterClose logic may not restore focus correctly because the original trigger element may no longer be in the same focusable context. The only reliable way to guarantee focus return in such layouts is to capture the trigger element before opening the modal and restore focus manually in the afterClose callback.
Modal, store a reference to the element that will open it.
const triggerRef = useRef(null);
const openModal = () => {
triggerRef.current = document.activeElement;
setVisible(true);
};
<Modal focusTriggerAfterClose={true} ...> (default is true, but set explicitly for clarity).afterClose. If you already have custom logic in afterClose that changes application state, add an explicit focus call after that logic.
const handleAfterClose = () => {
// your custom state updates
// ...
// restore focus
if (triggerRef.current) {
triggerRef.current.focus();
}
};
Enter, Space, or a mouse click.Esc, a mask click, or a button inside the modal.afterClose fires, check that document.activeElement equals the stored trigger element.If your application uses a custom getContainer function or a non‑standard portal hierarchy, let us know the exact structure so we can provide a more targeted focus‑restoration strategy.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.