diff --git a/app/Users/Controllers/UserAccountController.php b/app/Users/Controllers/UserAccountController.php
index 3dd13b851..83e942b04 100644
--- a/app/Users/Controllers/UserAccountController.php
+++ b/app/Users/Controllers/UserAccountController.php
@@ -7,6 +7,7 @@ use BookStack\Http\Controller;
 use BookStack\Permissions\PermissionApplicator;
 use BookStack\Settings\UserNotificationPreferences;
 use BookStack\Settings\UserShortcutMap;
+use BookStack\Uploads\ImageRepo;
 use BookStack\Users\UserRepo;
 use Closure;
 use Illuminate\Http\Request;
@@ -19,6 +20,7 @@ class UserAccountController extends Controller
     ) {
         $this->middleware(function (Request $request, Closure $next) {
             $this->preventGuestAccess();
+            $this->preventAccessInDemoMode();
             return $next($request);
         });
     }
@@ -35,6 +37,51 @@ class UserAccountController extends Controller
         ]);
     }
 
+    /**
+     * Show the profile form interface.
+     */
+    public function showProfile()
+    {
+        return view('users.account.profile', [
+            'model' => user(),
+            'category' => 'profile',
+        ]);
+    }
+
+    /**
+     * Handle the submission of the user profile form.
+     */
+    public function updateProfile(Request $request, ImageRepo $imageRepo)
+    {
+        $user = user();
+        $validated = $this->validate($request, [
+            'name'             => ['min:2', 'max:100'],
+            'email'            => ['min:2', 'email', 'unique:users,email,' . $user->id],
+            'language'         => ['string', 'max:15', 'alpha_dash'],
+            'profile_image'    => array_merge(['nullable'], $this->getImageValidationRules()),
+        ]);
+
+        $this->userRepo->update($user, $validated, userCan('users-manage'));
+
+        // Save profile image if in request
+        if ($request->hasFile('profile_image')) {
+            $imageUpload = $request->file('profile_image');
+            $imageRepo->destroyImage($user->avatar);
+            $image = $imageRepo->saveNew($imageUpload, 'user', $user->id);
+            $user->image_id = $image->id;
+            $user->save();
+        }
+
+        // Delete the profile image if reset option is in request
+        if ($request->has('profile_image_reset')) {
+            $imageRepo->destroyImage($user->avatar);
+            $user->image_id = 0;
+            $user->save();
+        }
+
+        return redirect('/my-account/profile');
+    }
+
     /**
      * Show the user-specific interface shortcuts.
      */
diff --git a/lang/en/preferences.php b/lang/en/preferences.php
index d112b9ebb..7774db570 100644
--- a/lang/en/preferences.php
+++ b/lang/en/preferences.php
@@ -35,5 +35,5 @@ return [
     'auth_change_password_success' => 'Password has been updated!',
 
     'profile' => 'Profile Details',
-    'profile_overview_desc' => ' Manage your user profile details including preferred language and authentication options.',
+    'profile_overview_desc' => 'Manage your user profile details including preferred language and authentication options.',
 ];
diff --git a/resources/views/users/account/profile.blade.php b/resources/views/users/account/profile.blade.php
new file mode 100644
index 000000000..4256df109
--- /dev/null
+++ b/resources/views/users/account/profile.blade.php
@@ -0,0 +1,107 @@
+@extends('users.account.layout')
+
+@section('main')
+
+    <section class="card content-wrap auto-height">
+        <form action="{{ url('/my-account/profile') }}" method="post" enctype="multipart/form-data">
+            {{ method_field('put') }}
+            {{ csrf_field() }}
+
+            <div class="flex-container-row gap-l items-center wrap justify-space-between">
+                <h1 class="list-heading">{{ trans('preferences.profile') }}</h1>
+                <div>
+                    <a href="{{ user()->getProfileUrl() }}" class="button outline">View Public Profile</a>
+                </div>
+            </div>
+
+            <p class="text-muted text-small mb-none">
+                Manage the details of your account that represent you to other users, in addition to
+                details that are used for communication and system personalisation.
+            </p>
+
+            <div class="setting-list">
+
+                <div class="flex-container-row gap-l items-center wrap">
+                    <div class="flex">
+                        <label class="setting-list-label" for="name">{{ trans('auth.name') }}</label>
+                        <p class="text-small mb-none">
+                            Configure your display name which will be visible to other users in the system
+                            within the activity you perform, and content you own.
+                        </p>
+                    </div>
+                    <div class="flex stretch-inputs">
+                        @include('form.text', ['name' => 'name'])
+                    </div>
+                </div>
+
+                <div>
+                    <div class="flex-container-row gap-l items-center wrap">
+                        <div class="flex">
+                            <label class="setting-list-label" for="email">{{ trans('auth.email') }}</label>
+                            <p class="text-small mb-none">
+                                This email will be used for notifications and, depending on active system authentication, system access.
+                            </p>
+                        </div>
+                        <div class="flex stretch-inputs">
+                            @include('form.text', ['name' => 'email', 'disabled' => !userCan('users-manage')])
+                        </div>
+                    </div>
+                    @if(!userCan('users-manage'))
+                        <p class="text-small text-muted">
+                            Unfortunately you don't have permission to change your email address.
+                            If you want to change this, you'd need to ask an administrator to change this for you.
+                        </p>
+                    @endif
+                </div>
+
+                <div class="grid half gap-xl">
+                    <div>
+                        <label for="user-avatar"
+                               class="setting-list-label">{{ trans('settings.users_avatar') }}</label>
+                        <p class="text-small">
+                            Select an image which will be used to represent yourself to others
+                            in the system. Ideally this image should be square and about 256px in width and height.
+                        </p>
+                    </div>
+                    <div>
+                        @include('form.image-picker', [
+                            'resizeHeight' => '512',
+                            'resizeWidth' => '512',
+                            'showRemove' => false,
+                            'defaultImage' => url('/user_avatar.png'),
+                            'currentImage' => user()->getAvatar(80),
+                            'currentId' => user()->image_id,
+                            'name' => 'profile_image',
+                            'imageClass' => 'avatar large'
+                        ])
+                    </div>
+                </div>
+
+                @include('users.parts.language-option-row', ['value' => old('language') ?? user()->getLocale()->appLocale()])
+
+            </div>
+
+            <div class="form-group text-right">
+                <button class="button">{{ trans('common.save') }}</button>
+            </div>
+
+        </form>
+    </section>
+
+    @if(userCan('users-manage'))
+        <section class="card content-wrap auto-height">
+            <div class="flex-container-row gap-l items-center wrap">
+                <div class="flex">
+                    <h2 class="list-heading">Administrator Options</h2>
+                    <p class="text-small">
+                        Additional administrator-level options, like role options, can be found for your user account in the
+                        <nobr>"Settings > Users"</nobr> area of the application.
+                    </p>
+                </div>
+                <div class="text-m-right">
+                    <a class="button outline" href="{{ user()->getEditUrl() }}">Open</a>
+                </div>
+            </div>
+        </section>
+    @endif
+@stop
diff --git a/routes/web.php b/routes/web.php
index a7d3534bd..3f1bcc07e 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -234,6 +234,8 @@ Route::middleware('auth')->group(function () {
 
     // User Account
     Route::get('/my-account', [UserControllers\UserAccountController::class, 'index']);
+    Route::get('/my-account/profile', [UserControllers\UserAccountController::class, 'showProfile']);
+    Route::put('/my-account/profile', [UserControllers\UserAccountController::class, 'updateProfile']);
     Route::get('/my-account/shortcuts', [UserControllers\UserAccountController::class, 'showShortcuts']);
     Route::put('/my-account/shortcuts', [UserControllers\UserAccountController::class, 'updateShortcuts']);
     Route::get('/my-account/notifications', [UserControllers\UserAccountController::class, 'showNotifications']);