How to Build a User Management System in the Berry React Admin Dashboard (Complete Guide + MUI Tutorial)How to Build a User Management System in the Berry React Admin Dashboard (Complete Guide + MUI Tutorial)

Hello guys, how are you? Welcome back to my blog therichpost.com. Today in this post I will tell you How to Build a User Management System in the Berry React Admin Dashboard (Complete Guide + MUI Tutorial).

Live Demo

For react js new comers, please check the below links:

  1. Reactjs Tutorials
  2. Nextjs
  3. Bootstrap 5
  4. React Free Ecommerce Templates
  5. React Free Admins
Berry free react material-ui admin template for easing and faster web development.
Berry free react material-ui admin template for easing and faster web development.

Project Setup

Clone the Berry free template:

git clone https://github.com/codedthemes/berry-free-react-admin-template
cd berry-free-react-admin-template
npm install
npm start

Step 1: Create the User Management Page(UserManagement.jsx) inside views folder and add below code inside it:

import React, { useEffect, useState } from "react";

import {
  Box,
  Typography,
  TextField,
  Button,
  Table,
  TableHead,
  TableBody,
  TableRow,
  TableCell,
  TableContainer,
  Paper,
  TablePagination,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  MenuItem,
  Select,
  FormControl,
  InputLabel,
  IconButton,
  Stack,
} from "@mui/material";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";

export default function UserManagement() {
  const [users, setUsers] = useState([]);
  const [query, setQuery] = useState("");

  // pagination state
  const [page, setPage] = useState(0);
  const [rowsPerPage, setRowsPerPage] = useState(8);

  // dialog state
  const [open, setOpen] = useState(false);
  const [editingUser, setEditingUser] = useState(null);

  // form state
  const [form, setForm] = useState({ name: "", email: "", role: "user", status: "active" });

  const STORAGE_KEY = "berry_users_v1";

  function loadFromStorage() {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (!raw) {
      const seed = [
        { id: 1, name: "Alice Johnson", email: "alice@example.com", role: "admin", status: "active" },
        { id: 2, name: "Bob Smith", email: "bob@example.com", role: "user", status: "inactive" },
        { id: 3, name: "Cecilia Brown", email: "cecilia@example.com", role: "user", status: "active" },
      ];
      localStorage.setItem(STORAGE_KEY, JSON.stringify(seed));
      return seed;
    }
    try {
      return JSON.parse(raw);
    } catch (e) {
      console.error("Failed to parse users from storage", e);
      return [];
    }
  }

  function saveToStorage(list) {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
  }

  useEffect(() => {
    setUsers(loadFromStorage());
  }, []);

  // --- Dialog handlers ---
  function handleOpenAdd() {
    setEditingUser(null);
    setForm({ name: "", email: "", role: "user", status: "active" });
    setOpen(true);
  }

  function handleOpenEdit(user) {
    setEditingUser(user);
    setForm({ name: user.name, email: user.email, role: user.role, status: user.status });
    setOpen(true);
  }

  function handleClose() {
    setOpen(false);
  }

  function handleDelete(id) {
    if (!window.confirm("Delete this user?")) return;
    const updated = users.filter((u) => u.id !== id);
    setUsers(updated);
    saveToStorage(updated);
    // adjust page if needed
    const maxPage = Math.max(0, Math.ceil(updated.length / rowsPerPage) - 1);
    setPage((p) => Math.min(p, maxPage));
  }

  function handleSubmit(e) {
    e && e.preventDefault();
    if (!form.name.trim() || !form.email.trim()) {
      alert("Name and email are required.");
      return;
    }

    if (editingUser) {
      const updated = users.map((u) => (u.id === editingUser.id ? { ...u, ...form } : u));
      setUsers(updated);
      saveToStorage(updated);
    } else {
      const nextId = users.length ? Math.max(...users.map((u) => u.id)) + 1 : 1;
      const newUser = { id: nextId, ...form };
      const updated = [newUser, ...users];
      setUsers(updated);
      saveToStorage(updated);
      setPage(0);
    }

    setOpen(false);
  }

  // --- Filtering + paging ---
  const filtered = users.filter((u) => {
    const q = query.toLowerCase();
    return (
      u.name.toLowerCase().includes(q) ||
      u.email.toLowerCase().includes(q) ||
      u.role.toLowerCase().includes(q) ||
      u.status.toLowerCase().includes(q)
    );
  });

  const emptyRows = Math.max(0, (1 + page) * rowsPerPage - filtered.length);
  const paginated = filtered.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage);

  // --- Table pagination handlers ---
  const handleChangePage = (event, newPage) => {
    setPage(newPage);
  };

  const handleChangeRowsPerPage = (event) => {
    setRowsPerPage(parseInt(event.target.value, 10));
    setPage(0);
  };

  return (
    <Box sx={{ p: 3 }}>
      <Stack direction="row" alignItems="center" justifyContent="space-between" mb={2}>
        <Typography variant="h5">User Management</Typography>
        <Stack direction="row" spacing={2}>
          <TextField
            size="small"
            placeholder="Search by name, email, role, status"
            value={query}
            onChange={(e) => { setQuery(e.target.value); setPage(0); }}
            sx={{ width: 320 }}
          />
          <Button variant="contained" onClick={handleOpenAdd}>Add User</Button>
        </Stack>
      </Stack>

      <Paper elevation={1}>
        <TableContainer>
          <Table>
            <TableHead>
              <TableRow>
                <TableCell>Name</TableCell>
                <TableCell>Email</TableCell>
                <TableCell>Role</TableCell>
                <TableCell>Status</TableCell>
                <TableCell align="right">Actions</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {paginated.length === 0 && (
                <TableRow>
                  <TableCell colSpan={5} align="center">
                    No users found.
                  </TableCell>
                </TableRow>
              )}

              {paginated.map((u) => (
                <TableRow key={u.id} hover>
                  <TableCell>{u.name}</TableCell>
                  <TableCell>{u.email}</TableCell>
                  <TableCell>{u.role}</TableCell>
                  <TableCell>{u.status}</TableCell>
                  <TableCell align="right">
                    <IconButton size="small" onClick={() => handleOpenEdit(u)} aria-label="edit">
                      <EditIcon fontSize="small" />
                    </IconButton>
                    <IconButton size="small" onClick={() => handleDelete(u.id)} aria-label="delete">
                      <DeleteIcon fontSize="small" />
                    </IconButton>
                  </TableCell>
                </TableRow>
              ))}

              {emptyRows > 0 && (
                <TableRow style={{ height: 53 * emptyRows }}>
                  <TableCell colSpan={5} />
                </TableRow>
              )}
            </TableBody>
          </Table>
        </TableContainer>

        <TablePagination
          component="div"
          count={filtered.length}
          page={page}
          onPageChange={handleChangePage}
          rowsPerPage={rowsPerPage}
          onRowsPerPageChange={handleChangeRowsPerPage}
          rowsPerPageOptions={[5, 8, 10, 25]}
        />
      </Paper>

      {/* Add / Edit Dialog */}
      <Dialog open={open} onClose={handleClose} fullWidth maxWidth="sm">
        <DialogTitle>{editingUser ? "Edit User" : "Add User"}</DialogTitle>
        <DialogContent>
          <Box component="form" onSubmit={handleSubmit} sx={{ mt: 1, display: "grid", gap: 2 }}>
            <TextField
              label="Full name"
              fullWidth
              value={form.name}
              onChange={(e) => setForm({ ...form, name: e.target.value })}
            />

            <TextField
              label="Email"
              fullWidth
              value={form.email}
              onChange={(e) => setForm({ ...form, email: e.target.value })}
            />

            <Stack direction="row" spacing={2}>
              <FormControl fullWidth>
                <InputLabel id="role-label">Role</InputLabel>
                <Select
                  labelId="role-label"
                  value={form.role}
                  label="Role"
                  onChange={(e) => setForm({ ...form, role: e.target.value })}
                >
                  <MenuItem value="user">User</MenuItem>
                  <MenuItem value="admin">Admin</MenuItem>
                  <MenuItem value="manager">Manager</MenuItem>
                </Select>
              </FormControl>

              <FormControl fullWidth>
                <InputLabel id="status-label">Status</InputLabel>
                <Select
                  labelId="status-label"
                  value={form.status}
                  label="Status"
                  onChange={(e) => setForm({ ...form, status: e.target.value })}
                >
                  <MenuItem value="active">Active</MenuItem>
                  <MenuItem value="inactive">Inactive</MenuItem>
                  <MenuItem value="pending">Pending</MenuItem>
                </Select>
              </FormControl>
            </Stack>
          </Box>
        </DialogContent>
        <DialogActions>
          <Button onClick={handleClose}>Cancel</Button>
          <Button onClick={handleSubmit} variant="contained">Save</Button>
        </DialogActions>
      </Dialog>
    </Box>
  );
}

Step 2: Add Route for the New Page src/routes/MainRoutes.js(just replace this):

import { lazy } from 'react';

// project imports
import MainLayout from 'layout/MainLayout';
import Loadable from 'ui-component/Loadable';

// dashboard routing
const DashboardDefault = Loadable(lazy(() => import('views/dashboard/Default')));

// utilities routing
const UtilsTypography = Loadable(lazy(() => import('views/utilities/Typography')));
const UtilsColor = Loadable(lazy(() => import('views/utilities/Color')));
const UtilsShadow = Loadable(lazy(() => import('views/utilities/Shadow')));

// sample page routing
const SamplePage = Loadable(lazy(() => import('views/sample-page')));

const UserManagement =  Loadable(lazy(() => import('views/UserManagement')));

// ==============================|| MAIN ROUTING ||============================== //

const MainRoutes = {
  path: '/',
  element: <MainLayout />,
  children: [
    {
      path: '/',
      element: <DashboardDefault />
    },
    {
      path: 'dashboard',
      children: [
        {
          path: 'default',
          element: <DashboardDefault />
        }
      ]
    },
    {
      path: 'typography',
      element: <UtilsTypography />
    },
    {
      path: 'color',
      element: <UtilsColor />
    },
    {
      path: 'shadow',
      element: <UtilsShadow />
    },
    {
      path: '/sample-page',
      element: <SamplePage />
    },
    {
      path: '/users',
      element: <UserManagement />
    }
  ]
};

export default MainRoutes;


Step 3: Add Sidebar Navigation Link src/menu-items/other.js (just replace this):

// assets
import { IconBrandChrome, IconHelp, IconUser } from '@tabler/icons-react';

// constant
const icons = { IconBrandChrome, IconHelp, IconUser };

// ==============================|| SAMPLE PAGE & DOCUMENTATION MENU ITEMS ||============================== //

const other = {
  id: 'sample-docs-roadmap',
  type: 'group',
  children: [
    {
      id: 'sample-page',
      title: 'Sample Page',
      type: 'item',
      url: '/sample-page',
      icon: icons.IconBrandChrome,
      breadcrumbs: false
    },
     {
      id: 'usermanagement',
      title: 'User Management',
      type: 'item',
      url: '/users',
      icon: icons.IconUser,
      breadcrumbs: false
    },
    {
      id: 'documentation',
      title: 'Documentation',
      type: 'item',
      url: 'https://codedthemes.gitbook.io/berry/',
      icon: icons.IconHelp,
      external: true,
      target: true
    }
  ]
};

export default other;

Step 4 : Just test your page:

npm start

You’ll see the new User Management page inside the Material UI layout, with a sidebar link that’s fully functional.

You’ve just extended a professional Material UI dashboard with your own custom page — this skill is essential for building scalable admin systems.

Ajay

Thanks

By therichpost

Hello to all. Welcome to therichpost.com. Myself Ajay Malhotra and I am freelance full stack developer. I love coding. I know WordPress, Core php, Angularjs, Angular 19, MedusaJs, Next.js, Bootstrap 5, Nodejs, Laravel, Codeigniter, Shopify, Squarespace, jQuery, Google Map Api, Vuejs, Reactjs, Big commerce etc.