diff --git a/platform/viewer/src/components/ViewportGrid.jsx b/platform/viewer/src/components/ViewportGrid.jsx
new file mode 100644
index 000000000..c14134bba
--- /dev/null
+++ b/platform/viewer/src/components/ViewportGrid.jsx
@@ -0,0 +1,100 @@
+/**
+ * CSS Grid Reference: http://grid.malven.co/
+ */
+import React, { useEffect } from 'react';
+import PropTypes from 'prop-types';
+//
+import ViewportPane from './ViewportPane.jsx';
+// import DefaultViewport from './DefaultViewport.js';
+// import EmptyViewport from './EmptyViewport.js';
+
+function ViewportGrid(props) {
+ const {
+ activeViewportIndex,
+ viewports,
+ displaySets,
+ numRows,
+ numColumns,
+ children,
+ } = props;
+
+ const rowSize = 100 / numRows;
+ const colSize = 100 / numColumns;
+
+ // viewportData --> displaySets
+
+ const getViewportPanes = () =>
+ viewports.map((viewport, viewportIndex) => {
+ const someId = viewport.displaySetId;
+ const displaySet = displaySets[someId];
+
+ if (!displaySet) {
+ // TODO: Empty Viewport
+ return null;
+ }
+
+ // const pluginName =
+ // !layout.plugin && displaySet && displaySet.plugin
+ // ? displaySet.plugin
+ // : layout.plugin;
+
+ // const ViewportComponent = _getViewportComponent(
+ // data, // Why do we pass this as `ViewportData`, when that's not really what it is?
+ // viewportIndex,
+ // children,
+
+ // pluginName,
+ // defaultPluginName
+ // );
+
+ return (
+ { /* setDisplaySet for Viewport */ }}
+ viewportIndex={viewportIndex}
+ isActive={activeViewportIndex === viewportIndex}
+ >
+ {/* {ViewportComponent} */}
+
+ );
+ });
+
+ // const ViewportPanes = React.useMemo(getViewportPanes, [
+ // viewportData,
+ // children,
+ // setViewportData,
+ // activeViewportIndex,
+ // ]);
+
+ return (
+
+ {/* {ViewportPanes} */}
+
+ );
+};
+
+ViewportGrid.propTypes = {
+ viewports: PropTypes.array.isRequired,
+ activeViewportIndex: PropTypes.number.isRequired,
+ children: PropTypes.node,
+ numRows: PropTypes.number.isRequired,
+ numColumns: PropTypes.number.isRequired,
+};
+
+ViewportGrid.defaultProps = {
+ viewports: [],
+ numRows: 1,
+ numColumns: 1,
+ activeViewportIndex: 0,
+};
+
+export default ViewportGrid;
diff --git a/platform/viewer/src/components/ViewportPane.jsx b/platform/viewer/src/components/ViewportPane.jsx
new file mode 100644
index 000000000..d3d4769d9
--- /dev/null
+++ b/platform/viewer/src/components/ViewportPane.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import { useDrop } from 'react-dnd';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+
+const ViewportPane = function(props) {
+ const { children, onDrop, viewportIndex } = props;
+ const [{ isHovered, isHighlighted }, drop] = useDrop({
+ accept: 'displayset',
+ drop: (displaySet, monitor) => {
+ const canDrop = monitor.canDrop();
+ const isOver = monitor.isOver();
+
+ if (canDrop && isOver && onDrop) {
+ const { StudyInstanceUID, displaySetInstanceUID } = displaySet;
+
+ onDrop({ viewportIndex, StudyInstanceUID, displaySetInstanceUID });
+ }
+ },
+ // Monitor, and collect props; returned as values by `useDrop`
+ collect: monitor => ({
+ isHighlighted: monitor.canDrop(),
+ isHovered: monitor.isOver(),
+ }),
+ });
+
+ return (
+
+ {children}
+
+ );
+};
+
+ViewportPane.propTypes = {
+ /** The ViewportComp */
+ children: PropTypes.node.isRequired,
+ /** Function that handles drop events */
+ onDrop: PropTypes.func.isRequired,
+ /** The "index" of the viewport in the current ViewportGrid */
+ viewportIndex: PropTypes.number.isRequired,
+};
+
+export default ViewportPane;
diff --git a/platform/viewer/src/components/getViewportsModule.js b/platform/viewer/src/components/getViewportsModule.js
new file mode 100644
index 000000000..af58c0ecb
--- /dev/null
+++ b/platform/viewer/src/components/getViewportsModule.js
@@ -0,0 +1,39 @@
+/**
+ *
+ *
+ * @param {*} plugin
+ * @param {*} viewportData
+ * @param {*} viewportIndex
+ * @param {*} children
+ * @returns
+ */
+function _getViewportComponent(
+ viewportData,
+ viewportIndex,
+ children,
+ availablePlugins,
+ pluginName,
+ defaultPluginName
+) {
+ if (viewportData.displaySet) {
+ pluginName = pluginName || defaultPluginName;
+ const ViewportComponent = availablePlugins[pluginName];
+
+ if (!ViewportComponent) {
+ throw new Error(
+ `No Viewport Component available for name ${pluginName}.
+ Available plugins: ${JSON.stringify(availablePlugins)}`
+ );
+ }
+
+ return (
+
+ );
+ }
+
+ return ;
+}
diff --git a/platform/viewer/src/routes/ModeRoute.js b/platform/viewer/src/routes/ModeRoute.js
index a4f691217..c4b5fdd6d 100644
--- a/platform/viewer/src/routes/ModeRoute.js
+++ b/platform/viewer/src/routes/ModeRoute.js
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useCallback } from 'react';
import { displaySetManager, ToolBarManager } from '@ohif/core';
import { useViewModel } from '@ohif/core';
import Compose from './Compose';
+import ViewportGrid from './../components/ViewportGrid.jsx';
export default function ModeRoute({
location,
@@ -85,10 +86,14 @@ export default function ModeRoute({
return (
+ {/* TODO: extensionManager is already provided to the extension module.
+ * Use it from there instead of passing as a prop here.
+ */}