diff --git a/ui/src/layout/Layout.js b/ui/src/layout/Layout.js
new file mode 100644
index 000000000..1a11c11f2
--- /dev/null
+++ b/ui/src/layout/Layout.js
@@ -0,0 +1,5 @@
+import React from 'react'
+import { Layout } from 'react-admin'
+import Menu from './Menu'
+
+export default (props) =>
diff --git a/ui/src/layout/Menu.js b/ui/src/layout/Menu.js
new file mode 100644
index 000000000..9e8229a51
--- /dev/null
+++ b/ui/src/layout/Menu.js
@@ -0,0 +1,73 @@
+// in src/Menu.js
+import React, { useState, createElement } from 'react'
+import { useSelector } from 'react-redux'
+import { useMediaQuery } from '@material-ui/core'
+import { useTranslate, MenuItemLink, getResources } from 'react-admin'
+import { withRouter } from 'react-router-dom'
+import LibraryMusicIcon from '@material-ui/icons/LibraryMusic'
+import ViewListIcon from '@material-ui/icons/ViewList'
+import SubMenu from './SubMenu'
+import inflection from 'inflection'
+
+const translatedResourceName = (resource, translate) =>
+ translate(`resources.${resource.name}.name`, {
+ smart_count: 2,
+ _:
+ resource.options && resource.options.label
+ ? translate(resource.options.label, {
+ smart_count: 2,
+ _: resource.options.label
+ })
+ : inflection.humanize(inflection.pluralize(resource.name))
+ })
+
+const Menu = ({ onMenuClick, dense, logout }) => {
+ const isXsmall = useMediaQuery((theme) => theme.breakpoints.down('xs'))
+ const open = useSelector((state) => state.admin.ui.sidebarOpen)
+ const translate = useTranslate()
+ const resources = useSelector(getResources)
+
+ const [state, setState] = useState({
+ menuLibrary: true
+ })
+
+ const handleToggle = (menu) => {
+ setState((state) => ({ ...state, [menu]: !state[menu] }))
+ }
+
+ const renderMenuItemLink = (resource) => (
+
+ }
+ onClick={onMenuClick}
+ sidebarIsOpen={open}
+ dense={dense}
+ />
+ )
+
+ const subItems = (subMenu) => (resource) =>
+ resource.hasList && resource.options && resource.options.subMenu === subMenu
+
+ return (
+
+ handleToggle('menuLibrary')}
+ isOpen={state.menuLibrary}
+ sidebarIsOpen={open}
+ name="Library"
+ icon={}
+ dense={dense}
+ >
+ {resources.filter(subItems('library')).map(renderMenuItemLink)}
+
+ {resources.filter(subItems(undefined)).map(renderMenuItemLink)}
+ {isXsmall && logout}
+
+ )
+}
+
+export default withRouter(Menu)
diff --git a/ui/src/layout/SubMenu.js b/ui/src/layout/SubMenu.js
new file mode 100644
index 000000000..1bd5a4afb
--- /dev/null
+++ b/ui/src/layout/SubMenu.js
@@ -0,0 +1,74 @@
+import React, { Fragment } from 'react'
+import ExpandMore from '@material-ui/icons/ExpandMore'
+import List from '@material-ui/core/List'
+import MenuItem from '@material-ui/core/MenuItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import Typography from '@material-ui/core/Typography'
+import Divider from '@material-ui/core/Divider'
+import Collapse from '@material-ui/core/Collapse'
+import Tooltip from '@material-ui/core/Tooltip'
+import { makeStyles } from '@material-ui/core/styles'
+import { useTranslate } from 'react-admin'
+
+const useStyles = makeStyles((theme) => ({
+ icon: { minWidth: theme.spacing(5) },
+ sidebarIsOpen: {
+ paddingLeft: 25,
+ transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms'
+ },
+ sidebarIsClosed: {
+ paddingLeft: 0,
+ transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms'
+ }
+}))
+
+const SubMenu = ({
+ handleToggle,
+ sidebarIsOpen,
+ isOpen,
+ name,
+ icon,
+ children,
+ dense
+}) => {
+ const translate = useTranslate()
+ const classes = useStyles()
+
+ const header = (
+
+ )
+
+ return (
+
+ {sidebarIsOpen || isOpen ? (
+ header
+ ) : (
+
+ {header}
+
+ )}
+
+
+ {children}
+
+
+
+
+ )
+}
+
+export default SubMenu
diff --git a/ui/src/layout/index.js b/ui/src/layout/index.js
index 5e18ae6cc..ebfb0760a 100644
--- a/ui/src/layout/index.js
+++ b/ui/src/layout/index.js
@@ -1,3 +1,4 @@
import Login from './Login'
+import Layout from './Layout'
-export { Login }
+export { Layout, Login }