diff --git a/.changeset/big-dryers-perform.md b/.changeset/big-dryers-perform.md
new file mode 100644
index 000000000..fc8b49f09
--- /dev/null
+++ b/.changeset/big-dryers-perform.md
@@ -0,0 +1,5 @@
+---
+'@cube-dev/ui-kit': minor
+---
+
+Render dialogs managed by useDialogContainer via provider.
diff --git a/src/components/Root.tsx b/src/components/Root.tsx
index e5c56178d..1f0790bbf 100644
--- a/src/components/Root.tsx
+++ b/src/components/Root.tsx
@@ -15,6 +15,7 @@ import { TOKENS } from '../tokens';
import { useViewportSize } from '../utils/react';
import { TrackingProps, TrackingProvider } from '../providers/TrackingProvider';
+import { DialogProvider } from './overlays/Dialog/index';
import { PortalProvider } from './portal';
import { GlobalStyles } from './GlobalStyles';
import { AlertDialogApiProvider } from './overlays/AlertDialog';
@@ -150,9 +151,11 @@ export function Root(allProps: CubeRootProps) {
/>
-
- {children}
-
+
+
+ {children}
+
+
diff --git a/src/components/overlays/Dialog/dialog-container.tsx b/src/components/overlays/Dialog/dialog-container.tsx
deleted file mode 100644
index 809b5c006..000000000
--- a/src/components/overlays/Dialog/dialog-container.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import React, { useState, useMemo } from 'react';
-
-import { useEvent } from '../../../_internal/index';
-
-import { DialogContainer } from './DialogContainer';
-
-/**
- * Generic hook to manage a dialog component.
- *
- * @param Component - A React component that represents the dialog content. It must accept props of type P.
- * @returns An object with `open` function to open the dialog with provided props and `rendered` JSX element to include in your component tree.
- */
-export function useDialogContainer
(Component: React.ComponentType
) {
- const [isOpen, setIsOpen] = useState(false);
- const [componentProps, setComponentProps] = useState
(null);
-
- // 'open' accepts props required by the Component and opens the dialog
- const open = useEvent((props: P) => {
- setComponentProps(props);
- setIsOpen(true);
- });
-
- const close = useEvent(() => {
- setIsOpen(false);
- });
-
- // Render the dialog only when componentProps is set
- const rendered = useMemo(() => {
- if (!componentProps) return null;
-
- return (
-
-
-
- );
- }, [componentProps, isOpen]);
-
- return { open, close, rendered };
-}
diff --git a/src/components/overlays/Dialog/index.ts b/src/components/overlays/Dialog/index.ts
index 3eecaa079..7865f04c8 100644
--- a/src/components/overlays/Dialog/index.ts
+++ b/src/components/overlays/Dialog/index.ts
@@ -2,4 +2,4 @@ export * from './DialogContainer';
export * from './DialogForm';
export * from './DialogTrigger';
export * from './Dialog';
-export * from './dialog-container';
+export * from './use-dialog-container';
diff --git a/src/components/overlays/Dialog/use-dialog-container.tsx b/src/components/overlays/Dialog/use-dialog-container.tsx
new file mode 100644
index 000000000..97bf752cd
--- /dev/null
+++ b/src/components/overlays/Dialog/use-dialog-container.tsx
@@ -0,0 +1,118 @@
+import React, {
+ createContext,
+ useState,
+ useContext,
+ useCallback,
+ useEffect,
+ useRef,
+ useMemo,
+ ReactNode,
+} from 'react';
+
+import { useEvent } from '../../../_internal/index';
+
+import { DialogContainer } from './DialogContainer';
+
+// Define a context type to handle dialog operations
+interface DialogContextType {
+ addDialog: (element: React.ReactNode) => number;
+ removeDialog: (id: number) => void;
+ updateDialog: (id: number, element: React.ReactNode) => void;
+}
+
+// Create a context for dialogs
+const DialogContext = createContext(null);
+
+// Provider component that renders dialogs outside the normal tree
+export const DialogProvider = ({ children }: { children: ReactNode }) => {
+ const [dialogs, setDialogs] = useState<
+ Array<{ id: number; element: React.ReactNode }>
+ >([]);
+
+ const addDialog = useCallback((element: React.ReactNode) => {
+ // Create a unique id for the dialog
+ const id = Date.now() + Math.random();
+
+ setDialogs((prev) => [...prev, { id, element }]);
+
+ return id;
+ }, []);
+
+ const removeDialog = useCallback((id: number) => {
+ setDialogs((prev) => prev.filter((dialog) => dialog.id !== id));
+ }, []);
+
+ const updateDialog = useCallback((id: number, element: React.ReactNode) => {
+ setDialogs((prev) =>
+ prev.map((dialog) => (dialog.id === id ? { id, element } : dialog)),
+ );
+ }, []);
+
+ return (
+
+ {children}
+ {dialogs.map(({ id, element }) => (
+ {element}
+ ))}
+
+ );
+};
+
+/**
+ * Custom hook to open a dialog using a global context.
+ *
+ * @param Component - A React component representing the dialog content. It receives props of type P.
+ * @returns An object with an `open` function to display the dialog and a generic `close` function.
+ */
+export function useDialogContainer(Component: React.ComponentType
) {
+ const context = useContext(DialogContext);
+ if (!context) {
+ throw new Error('useDialogContainer must be used within a DialogProvider');
+ }
+ const { addDialog, removeDialog, updateDialog } = context;
+
+ const [isOpen, setIsOpen] = useState(false);
+ const [componentProps, setComponentProps] = useState
(null);
+
+ const open = useEvent((props: P) => {
+ setComponentProps(props);
+ setIsOpen(true);
+ });
+
+ const close = useEvent(() => {
+ setIsOpen(false);
+ });
+
+ const renderedElement = useMemo(
+ () =>
+ componentProps ? (
+
+ {componentProps && }
+
+ ) : null,
+ [isOpen, componentProps],
+ );
+
+ const dialogIdRef = useRef(null);
+
+ useEffect(() => {
+ // Register the dialog on mount
+ dialogIdRef.current = addDialog(renderedElement);
+
+ return () => {
+ // Remove the dialog on unmount
+ if (dialogIdRef.current !== null) {
+ removeDialog(dialogIdRef.current);
+ }
+ };
+ }, []);
+
+ useEffect(() => {
+ // Update the dialog when the rendered element changes
+ if (dialogIdRef.current !== null) {
+ updateDialog(dialogIdRef.current, renderedElement);
+ }
+ }, [renderedElement]);
+
+ return { open, close };
+}