diff --git a/src/pages/User/List/User.jsx b/src/pages/User/List/User.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..54266a29d26141905ed5b6d8ec1d198f3187df5c
--- /dev/null
+++ b/src/pages/User/List/User.jsx
@@ -0,0 +1,99 @@
+import { zodResolver } from '@hookform/resolvers/zod';
+import React, { useState } from 'react';
+import { FormProvider, useForm } from 'react-hook-form';
+import { RiDeleteBinLine } from 'react-icons/ri';
+import { z } from 'zod';
+import Input from '../../../components/form/Input';
+import Select from '../../../components/form/Select';
+import Submit from '../../../components/form/Submit';
+import { useAuth } from '../../../contexts/Auth/AuthState';
+import { ROLES } from '../UserTypes';
+import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling';
+
+
+function User({ user, idx, handleDelete }) {
+  // #################################
+  // VALIDATION SCHEMA
+  // #################################
+  const schema = z.object({
+    name: z.string().min(2),
+    username: z.string().min(2),
+    email: z.string().min(1).email(),
+    verified: z.string().transform((val) => val === 'true' ? true : false),
+    role: z.coerce.number()
+  });
+  // #################################
+  // HOOKS
+  // #################################
+  // ### CONNECT AUTH CONTEXT
+  const { currentUser, update } = useAuth();
+  const [updatedUser, setUpdatedUser] = useState(user);
+
+  // ### PREPARE FORM
+  const methods = useForm({
+    resolver: zodResolver(schema),
+    mode: 'onBlur',
+    defaultValues: user
+  });
+
+
+  // const id = useId();
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // ### HANDLE SUBMITTING FORM
+  async function handleSendForm(inputs) {
+    // save to db
+    try {
+      // send data
+      const result = await update(user._id, inputs);
+      setUpdatedUser(result.data.document);
+      // set flash msg
+      setFlashMsg(result.data?.message);
+    } catch (error) {
+      // catch the error
+      mergeBackendValidation(error.response.status, error.response.data, methods.setError);
+    }
+  }
+
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <div className='flex items-center bg-white border border-UhhLightGrey rounded-lg shadow-lg p-3 m-3'>
+      <div>
+        {currentUser?.role >= 2 ?
+          <>
+            <h2 className='text-Uhh-Grey font-bold text-2xl flex justify-end'>
+              <RiDeleteBinLine className='cursor-pointer hover:text-UhhRed' title='delete user' onClick={() => handleDelete(user.id, idx)} />
+            </h2>
+            <FormProvider {...methods} >
+              <form onSubmit={methods.handleSubmit(handleSendForm)} noValidate>
+                <Input name='name' type='text' title='name' required={true} />
+
+                <Input name='username' type='text' title='username' required={true} />
+
+                <Input name='email' type='email' title='eMail' required={true} />
+
+                <Select title="verified" name="verified" options={[{ _id: true, title: 'verified' }, { _id: false, title: 'not verified' }]} required={true} />
+
+                <Select title="Role" name="role" options={ROLES} required={true} />
+
+                <Submit value='save' />
+              </form>
+            </FormProvider>
+          </>
+          :
+          <>
+            <h2 className='text-Uhh-Grey font-bold text-2xl'>{user.fullname}</h2>
+            <p className='text-sm'>{user.email}</p>
+          </>
+        }
+      </div>
+    </div>
+  );
+}
+
+export default React.memo(User);
\ No newline at end of file
diff --git a/src/pages/User/List/Users.jsx b/src/pages/User/List/Users.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..2ddb103b0671d16f5212b770571d57b311086e9e
--- /dev/null
+++ b/src/pages/User/List/Users.jsx
@@ -0,0 +1,81 @@
+import React, { useEffect, useState } from 'react';
+import { Helmet } from 'react-helmet-async';
+import Heading from '../../../components/font/Heading';
+import api from '../../../utils/AxiosConfig';
+import User from './User';
+import { mergeBackendValidation, setFlashMsg } from '/src/utils/ErrorHandling';
+
+
+function Users() {
+  // #################################
+  // HOOKS
+  // #################################
+  // ### USERS
+  const [users, setUsers] = useState();
+
+  // ### FETCH USERS
+  useEffect(() => {
+    // mount
+    const controller = new AbortController();
+    const getUsers = async () => {
+      try {
+        const result = await api.get('/users', {
+          signal: controller.signal
+        });
+        setUsers(result?.data);
+      } catch (error) {
+        mergeBackendValidation(error.response.status, error.response.data);
+      }
+    };
+    getUsers();
+
+    return () => {
+      // on unmount abort request
+      controller.abort();
+    };
+  }, []);
+
+  // #################################
+  // FUNCTIONS
+  // #################################
+  // ### DELETE USERS
+  const handleDelete = async (id, idx) => {
+    try {
+      // delete in backend
+      const result = await api.delete(`/users/${id}`);
+      // delete in frontend
+      const list = [...users];
+      list.splice(idx, 1);
+      setUsers(list);
+      setFlashMsg(result.data?.message);
+    } catch (error) {
+      mergeBackendValidation(error.response.status, error.response.data, methods.setError);
+    }
+  };
+
+  // #################################
+  // OUTPUT
+  // #################################
+  return (
+    <>
+      {/* render page title */}
+      <Helmet><title>[{import.meta.env.VITE_APP_NAME}] Users</title></Helmet>
+
+      <Heading level="1" className="col-span-2">Registered Users</Heading>
+      <div>
+        {users?.length
+          ? (
+            <div className='flex flex-wrap justify-items-stretch'>
+              {users.map((user, idx) =>
+                <User key={user._id} idx={idx} user={user} handleDelete={handleDelete} />
+              )}
+
+            </div>
+          ) : <p>No users to display</p>
+        }
+      </div>
+    </>
+  );
+}
+
+export default React.memo(Users);
diff --git a/src/pages/User/UserTypes.js b/src/pages/User/UserTypes.js
new file mode 100644
index 0000000000000000000000000000000000000000..1305abdcc109d7067ec17f5dd7992cf9bda353fb
--- /dev/null
+++ b/src/pages/User/UserTypes.js
@@ -0,0 +1,9 @@
+// the _id represents the order ranking the role
+// the lower the _id, the lower the permissions
+export const ROLES = [
+  { _id: 0, title: 'User' },
+  { _id: 1, title: 'Editor' },
+  { _id: 2, title: 'Poweruser' },
+  { _id: 3, title: 'Lead' },
+  { _id: 4, title: 'Admin' }
+];
\ No newline at end of file
diff --git a/src/routes/Sitemap.jsx b/src/routes/Sitemap.jsx
index b8da28fc13b4d6ceac43425ecf2829928fbe20e4..253a4b5e16c59a61f26fe22666ebae9217fc10a1 100644
--- a/src/routes/Sitemap.jsx
+++ b/src/routes/Sitemap.jsx
@@ -38,6 +38,10 @@ export const sitemap = [{
 
         ]
       },
+      // USER
+      {
+        title: 'Users', path: '/users', element: loadComponent('User/List/Users', true, true), handle: { crumb: () => <Link to="/users">Users</Link> }
+      },
       // PROFILE
       {
         title: 'Profile', path: '/profile', handle: { crumb: () => <Link to="/profile">Profile</Link> },