Skip to main content

Overview

The MasterLayout component provides the primary application structure for Cat Web. It includes a top AppBar with branding and user information, a responsive navigation drawer, and a content container for nested routes. Source: src/components/MasterLayout.tsx

Key Features

  • Top AppBar with application branding and user profile
  • Right-side navigation drawer with menu items
  • Responsive design with Material-UI components
  • Integration with React Router for navigation
  • Auth0 integration for authentication state
  • Logout functionality
  • Inventory catalogue branding

Props

This component does not accept any props. It’s a self-contained layout component.
const MasterLayout: React.FC = () => { ... }

Layout Structure

The component uses a flexbox layout with three main sections:
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
    {/* 1. AppBar - Fixed at top */}
    <AppBar position="static">
        ...
    </AppBar>
    
    {/* 2. Drawer - Slides in from right */}
    <Drawer anchor="right" open={drawerOpen}>
        ...
    </Drawer>
    
    {/* 3. Content - Grows to fill remaining space */}
    <Container component="main" sx={{ flexGrow: 1, py: 3 }}>
        <Outlet />
    </Container>
</Box>

AppBar Section

The top bar displays application branding and authentication-dependent content:

Branding

<Box sx={{ display: 'flex', alignItems: 'center' }}>
    <Inventory2Icon sx={{ mr: 1 }} />
    <Typography variant="h6" component="div">
        Inventory catalogue
    </Typography>
</Box>

Authenticated User Display

When authenticated, the user’s name and menu icon are shown:
{isAuthenticated && !isLoading && (
    <Box display="flex" alignItems="center">
        <Typography variant="body1" sx={{ mr: 2 }}>
            {user?.name}
        </Typography>
        <IconButton
            color="inherit"
            aria-label="open drawer"
            edge="end"
            onClick={toggleDrawer}
        >
            <MenuIcon />
        </IconButton>
    </Box>
)}

Unauthenticated State

For unauthenticated users, a login button is displayed:
{!isAuthenticated && !isLoading && (
    <Box display="flex" alignItems="center">
        <IconButton
            color="inherit"
            onClick={()=> loginWithPopup()}
        >
            Login
        </IconButton>
    </Box>
)}
The drawer slides in from the right side and contains navigation links:

Drawer Configuration

<Drawer
    anchor="right"
    open={drawerOpen}
    onClose={toggleDrawer}
>
    <Box
        sx={{ width: 250 }}
        role="presentation"
        onClick={toggleDrawer}
    >
        <List>
            {/* Navigation items */}
        </List>
    </Box>
</Drawer>
The drawer includes four navigation items:
  1. Home - Links to /
    <ListItem>
        <Link to="/" style={{ textDecoration: 'none', color: 'inherit', display: 'block', width: '100%' }}>
            <ListItemButton>
                <ListItemIcon>
                    <HomeIcon />
                </ListItemIcon>
                <ListItemText primary="Home" />
            </ListItemButton>
        </Link>
    </ListItem>
    
  2. Inventory - Links to /dashboard
  3. API tests - Links to /api-tests
  4. Logout - Triggers Auth0 logout

Logout Functionality

<ListItemButton onClick={() => {
    logout({ logoutParams: { returnTo: window.location.origin } });
}}>
    <ListItemIcon>
        <LogoutIcon />
    </ListItemIcon>
    <ListItemText primary="Logout" />
</ListItemButton>

State Management

The component manages drawer state with React useState:
const [drawerOpen, setDrawerOpen] = useState(false);

const toggleDrawer = () => {
    setDrawerOpen(!drawerOpen);
};

Auth0 Integration

The component uses the useAuth0 hook to access authentication state:
const { user, isAuthenticated, isLoading, loginWithPopup, logout } = useAuth0();

Usage with React Router

Basic Setup

import { BrowserRouter, Routes, Route } from 'react-router-dom';
import MasterLayout from './components/MasterLayout';
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import ApiTesting from './components/ApiTesting';

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element={<MasterLayout />}>
                    <Route index element={<Home />} />
                    <Route path="dashboard" element={<Dashboard />} />
                    <Route path="api-tests" element={<ApiTesting />} />
                </Route>
            </Routes>
        </BrowserRouter>
    );
}

With AuthGuard Protection

import AuthGuard from './components/AuthGuard';
import MasterLayout from './components/MasterLayout';

function App() {
    return (
        <Routes>
            <Route path="/" element={
                <AuthGuard component={<MasterLayout />} />
            }>
                <Route path="dashboard" element={<Dashboard />} />
                <Route path="api-tests" element={<ApiTesting />} />
            </Route>
        </Routes>
    );
}

Material-UI Components Used

The component leverages numerous Material-UI components:

Layout & Structure

  • AppBar: Top application bar
  • Toolbar: Container for AppBar content
  • Drawer: Sliding navigation panel
  • Box: Flexible container for layout
  • Container: Content container with max-width
  • List: Container for navigation items
  • ListItem: Individual list item wrapper
  • ListItemButton: Clickable list item
  • ListItemIcon: Icon container in list items
  • ListItemText: Text content in list items

UI Elements

  • IconButton: Clickable icon buttons
  • Typography: Styled text components

Icons from @mui/icons-material

  • Inventory2Icon: App branding and inventory navigation
  • MenuIcon: Drawer toggle button
  • HomeIcon: Home navigation
  • LogoutIcon: Logout action
  • ApiIcon: API testing navigation

Responsive Design

The layout automatically adapts to different screen sizes:
  • Mobile: Drawer provides compact navigation
  • Desktop: Full AppBar with user information visible
  • Flexbox: Content grows to fill available vertical space
sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}

Complete Source Code

import React, { useState } from 'react';
import { 
    AppBar, 
    Toolbar, 
    IconButton, 
    Typography, 
    Drawer, 
    List, 
    ListItem,
    ListItemButton,
    ListItemIcon, 
    ListItemText, 
    Box, 
    Container
} from '@mui/material';

import Inventory2Icon from '@mui/icons-material/Inventory2';
import MenuIcon from '@mui/icons-material/Menu';
import HomeIcon from '@mui/icons-material/Home';
import LogoutIcon from '@mui/icons-material/Logout';
import ApiIcon from '@mui/icons-material/Api';

import { Outlet, Link } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';

const MasterLayout: React.FC = () => {
const [drawerOpen, setDrawerOpen] = useState(false);
const { user, isAuthenticated, isLoading, loginWithPopup, logout } = useAuth0();

const toggleDrawer = () => {
    setDrawerOpen(!drawerOpen);
};

return (
    <Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
        <AppBar position="static">
            <Toolbar sx={{ justifyContent: 'space-between' }}>
                <Box sx={{ display: 'flex', alignItems: 'center' }}>
                    <Inventory2Icon sx={{ mr: 1 }} />
                    <Typography variant="h6" component="div">
                        Inventory catalogue
                    </Typography>
                </Box>
                {isAuthenticated && !isLoading && (
                    <Box display="flex" alignItems="center">
                        <Box sx={{ display: 'flex', alignItems: 'center' }}>
                            <Typography variant="body1" sx={{ mr: 2 }}>
                                {user?.name}
                            </Typography>
                        </Box>
                        <IconButton
                            color="inherit"
                            aria-label="open drawer"
                            edge="end"
                            onClick={toggleDrawer}
                        >
                            <MenuIcon />
                        </IconButton>
                    </Box>
                )}
                {!isAuthenticated && !isLoading && (
                    <Box display="flex" alignItems="center">
                        <IconButton
                            color="inherit"
                            aria-label="open drawer"
                            edge="end"
                            size="small"
                            onClick={()=> loginWithPopup()}
                        >
                            Login
                        </IconButton>
                    </Box>
                )}
            </Toolbar>
        </AppBar>

        <Drawer
            anchor="right"
            open={drawerOpen}
            onClose={toggleDrawer}
        >
            <Box
                sx={{ width: 250 }}
                role="presentation"
                onClick={toggleDrawer}
            >
                <List>
                    <ListItem>
                        <Link to="/" style={{ textDecoration: 'none', color: 'inherit', display: 'block', width: '100%' }}>
                            <ListItemButton>
                                <ListItemIcon>
                                    <HomeIcon />
                                </ListItemIcon>
                                <ListItemText primary="Home" />
                            </ListItemButton>
                        </Link>
                    </ListItem>
                    <ListItem>
                        <Link to="/dashboard" style={{ textDecoration: 'none', color: 'inherit', display: 'block', width: '100%' }}>
                            <ListItemButton>
                                <ListItemIcon>
                                    <Inventory2Icon />
                                </ListItemIcon>
                                <ListItemText primary="Inventory" />
                            </ListItemButton>
                        </Link>
                    </ListItem>
                    <ListItem>
                        <Link to="/api-tests" style={{ textDecoration: 'none', color: 'inherit', display: 'block', width: '100%' }}>
                            <ListItemButton>
                                <ListItemIcon>
                                    <ApiIcon />
                                </ListItemIcon>
                                <ListItemText primary="API tests" />
                            </ListItemButton>
                        </Link>
                    </ListItem>
                    <ListItem>
                        <ListItemButton onClick={() => {
                            logout({ logoutParams: { returnTo: window.location.origin } });
                        }}>
                            <ListItemIcon>
                                <LogoutIcon />
                            </ListItemIcon>
                            <ListItemText primary="Logout" />
                        </ListItemButton>
                    </ListItem>
                </List>
            </Box>
        </Drawer>

        <Container component="main" sx={{ flexGrow: 1, py: 3 }}>
            <Outlet />
        </Container>
    </Box>
);
};

export default MasterLayout;

Best Practices

  1. Consistent Layout: Use MasterLayout as the parent route for all authenticated pages
  2. Drawer State: The drawer automatically closes when clicking menu items
  3. Navigation Links: Use React Router’s <Link> component for client-side navigation
  4. Logout Redirect: Always specify returnTo parameter for proper logout redirect
  5. Loading States: Conditionally render UI based on isLoading to prevent flashing

Build docs developers (and LLMs) love