diff --git a/platform/ui/index.js b/platform/ui/index.js
index d9fe40b36..6990c8d25 100644
--- a/platform/ui/index.js
+++ b/platform/ui/index.js
@@ -1,4 +1,10 @@
import * as utils from './src/utils/';
+import {
+ ModalProvider,
+ ModalConsumer,
+ useModal,
+ withModal,
+} from './src/contextProviders';
export {
Button,
@@ -12,7 +18,5 @@ export {
ThemeWrapper,
Typography,
} from './src/components';
-
-export { utils };
-
export { StudyList } from './src/views';
+export { ModalProvider, ModalConsumer, useModal, withModal, utils };
diff --git a/platform/ui/src/contextProviders/ModalProvider.js b/platform/ui/src/contextProviders/ModalProvider.js
new file mode 100644
index 000000000..e8d65635d
--- /dev/null
+++ b/platform/ui/src/contextProviders/ModalProvider.js
@@ -0,0 +1,134 @@
+import React, {
+ useState,
+ createContext,
+ useContext,
+ useEffect,
+ useCallback,
+} from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+
+const ModalContext = createContext(null);
+const { Provider } = ModalContext;
+
+export const useModal = () => useContext(ModalContext);
+
+/**
+ * UI Modal
+ *
+ * @typedef {Object} ModalProps
+ * @property {ReactElement|HTMLElement} [content=null] Modal content.
+ * @property {Object} [contentProps=null] Modal content props.
+ * @property {boolean} [shouldCloseOnEsc=false] Modal is dismissible via the esc key.
+ * @property {boolean} [isOpen=true] Make the Modal visible or hidden.
+ * @property {boolean} [closeButton=true] Should the modal body render the close button.
+ * @property {string} [title=null] Should the modal render the title independently of the body content.
+ * @property {string} [customClassName=null] The custom class to style the modal.
+ */
+
+const ModalProvider = ({ children, modal: Modal, service }) => {
+ const DEFAULT_OPTIONS = {
+ content: null,
+ contentProps: null,
+ shouldCloseOnEsc: false,
+ isOpen: true,
+ closeButton: true,
+ title: null,
+ customClassName: '',
+ };
+
+ const [options, setOptions] = useState(DEFAULT_OPTIONS);
+
+ /**
+ * Sets the implementation of a modal service that can be used by extensions.
+ *
+ * @returns void
+ */
+ useEffect(() => {
+ if (service) {
+ service.setServiceImplementation({ hide, show });
+ }
+ }, [hide, service, show]);
+
+ /**
+ * Show the modal and override its configuration props.
+ *
+ * @param {ModalProps} props { content, contentProps, shouldCloseOnEsc, isOpen, closeButton, title, customClassName }
+ * @returns void
+ */
+ const show = useCallback(props => setOptions({ ...options, ...props }), [
+ options,
+ ]);
+
+ /**
+ * Hide the modal and set its properties to default.
+ *
+ * @returns void
+ */
+ const hide = useCallback(() => setOptions(DEFAULT_OPTIONS), [
+ DEFAULT_OPTIONS,
+ ]);
+
+ const {
+ content: ModalContent,
+ contentProps,
+ isOpen,
+ title,
+ customClassName,
+ shouldCloseOnEsc,
+ closeButton,
+ } = options;
+
+ return (
+
+ {ModalContent && (
+
+
+
+ )}
+ {children}
+
+ );
+};
+
+/**
+ * Higher Order Component to use the modal methods through a Class Component.
+ *
+ * @returns
+ */
+export const withModal = Component => {
+ return function WrappedComponent(props) {
+ const { show, hide } = useModal();
+ return ;
+ };
+};
+
+ModalProvider.defaultProps = {
+ service: null,
+};
+
+ModalProvider.propTypes = {
+ children: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.node),
+ PropTypes.node,
+ ]).isRequired,
+ modal: PropTypes.oneOfType([
+ PropTypes.arrayOf(PropTypes.node),
+ PropTypes.node,
+ PropTypes.func,
+ ]).isRequired,
+ service: PropTypes.shape({
+ setServiceImplementation: PropTypes.func,
+ }),
+};
+
+export default ModalProvider;
+
+export const ModalConsumer = ModalContext.Consumer;
diff --git a/platform/ui/src/contextProviders/index.js b/platform/ui/src/contextProviders/index.js
new file mode 100644
index 000000000..f2e6811fb
--- /dev/null
+++ b/platform/ui/src/contextProviders/index.js
@@ -0,0 +1,6 @@
+export {
+ default as ModalProvider,
+ useModal,
+ withModal,
+ ModalConsumer,
+} from './ModalProvider.js';