diff --git a/modules/user/src/AccountForm.php b/modules/user/src/AccountForm.php
index f092f91e90..1b84add5eb 100644
--- a/modules/user/src/AccountForm.php
+++ b/modules/user/src/AccountForm.php
@@ -91,13 +91,15 @@ abstract class AccountForm extends ContentEntityForm {
     // and the user performing the edit has 'administer users' permission.
     // This allows users without email address to be edited and deleted.
     // Also see \Drupal\user\Plugin\Validation\Constraint\UserMailRequired.
-    $form['account']['mail'] = [
-      '#type' => 'email',
-      '#title' => $this->t('Email address'),
-      '#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'),
-      '#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
-      '#default_value' => (!$register ? $account->getEmail() : ''),
-    ];
+    if ($register) {
+      $form['account']['mail'] = [
+        '#type' => 'email',
+        '#title' => $this->t('Email address'),
+        '#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'),
+        '#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
+        '#default_value' => (!$register ? $account->getEmail() : ''),
+      ];
+    }
 
     // Only show name field on registration form or user can change own username.
     $form['account']['name'] = [
@@ -116,51 +118,7 @@ abstract class AccountForm extends ContentEntityForm {
       '#access' => $account->name->access('edit'),
     ];
 
-    // Display password field only for existing users or when user is allowed to
-    // assign a password during registration.
-    if (!$register) {
-      $form['account']['pass'] = [
-        '#type' => 'password_confirm',
-        '#size' => 25,
-        '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
-      ];
-
-      // To skip the current password field, the user must have logged in via a
-      // one-time link and have the token in the URL. Store this in $form_state
-      // so it persists even on subsequent Ajax requests.
-      if (!$form_state->get('user_pass_reset') && ($token = $this->getRequest()->get('pass-reset-token'))) {
-        $session_key = 'pass_reset_' . $account->id();
-        $user_pass_reset = isset($_SESSION[$session_key]) && hash_equals($_SESSION[$session_key], $token);
-        $form_state->set('user_pass_reset', $user_pass_reset);
-      }
-
-      // The user must enter their current password to change to a new one.
-      if ($user->id() == $account->id()) {
-        $form['account']['current_pass'] = [
-          '#type' => 'password',
-          '#title' => $this->t('Current password'),
-          '#size' => 25,
-          '#access' => !$form_state->get('user_pass_reset'),
-          '#weight' => -5,
-          // Do not let web browsers remember this password, since we are
-          // trying to confirm that the person submitting the form actually
-          // knows the current one.
-          '#attributes' => ['autocomplete' => 'off'],
-        ];
-        $form_state->set('user', $account);
-
-        // The user may only change their own password without their current
-        // password if they logged in via a one-time login link.
-        if (!$form_state->get('user_pass_reset')) {
-          $form['account']['current_pass']['#description'] = $this->t('Required if you want to change the %mail or %pass below. <a href=":request_new_url" title="Send password reset instructions via email.">Reset your password</a>.', [
-            '%mail' => $form['account']['mail']['#title'],
-            '%pass' => $this->t('Password'),
-            ':request_new_url' => $this->url('user.pass'),
-          ]);
-        }
-      }
-    }
-    elseif (!$config->get('verify_mail') || $admin_create) {
+    if (!$config->get('verify_mail') || $admin_create) {
       $form['account']['pass'] = [
         '#type' => 'password_confirm',
         '#size' => 25,
diff --git a/modules/user/src/Controller/UserController.php b/modules/user/src/Controller/UserController.php
index 53eeb049bd..681d18ac6c 100644
--- a/modules/user/src/Controller/UserController.php
+++ b/modules/user/src/Controller/UserController.php
@@ -235,7 +235,7 @@ class UserController extends ControllerBase {
       $token = Crypt::randomBytesBase64(55);
       $_SESSION['pass_reset_' . $user->id()] = $token;
       return $this->redirect(
-        'entity.user.edit_form',
+        'user.edit_pass.form',
         ['user' => $user->id()],
         [
           'query' => ['pass-reset-token' => $token],
diff --git a/modules/user/src/Entity/User.php b/modules/user/src/Entity/User.php
index 8ed8dfdac8..e244b4bf2c 100644
--- a/modules/user/src/Entity/User.php
+++ b/modules/user/src/Entity/User.php
@@ -40,6 +40,7 @@ use Drupal\user\UserInterface;
  *     },
  *     "form" = {
  *       "default" = "Drupal\user\ProfileForm",
+ *       "email" = "Drupal\user\Form\UserEmailChangeForm",
  *       "cancel" = "Drupal\user\Form\UserCancelForm",
  *       "register" = "Drupal\user\RegisterForm"
  *     },
@@ -58,6 +59,8 @@ use Drupal\user\UserInterface;
  *   links = {
  *     "canonical" = "/user/{user}",
  *     "edit-form" = "/user/{user}/edit",
+ *     "edit-pass-form" = "/user/{user}/edit-pass",
+ *     "edit-email-form" = "/user/{user}/edit-email",
  *     "cancel-form" = "/user/{user}/cancel",
  *     "collection" = "/admin/people",
  *   },
diff --git a/modules/user/src/Entity/UserRouteProvider.php b/modules/user/src/Entity/UserRouteProvider.php
index d1e967184e..448f487bc1 100644
--- a/modules/user/src/Entity/UserRouteProvider.php
+++ b/modules/user/src/Entity/UserRouteProvider.php
@@ -46,6 +46,16 @@ class UserRouteProvider implements EntityRouteProviderInterface {
       ->setRequirement('_entity_access', 'user.delete');
     $route_collection->add('entity.user.cancel_form', $route);
 
+    $route = (new Route('/user/{user}/edit-email'))
+      ->setDefaults([
+        '_title' => 'Change email',
+        '_entity_form' => 'user.email',
+      ])
+      ->setOption('_admin_route', TRUE)
+      ->setRequirement('user', '\d+')
+      ->setRequirement('_entity_access', 'user.update');
+    $route_collection->add('entity.user.change_email_form', $route);
+
     return $route_collection;
   }
 
diff --git a/modules/user/src/Form/UserEmailChangeForm.php b/modules/user/src/Form/UserEmailChangeForm.php
new file mode 100644
index 0000000000..1d41bc6594
--- /dev/null
+++ b/modules/user/src/Form/UserEmailChangeForm.php
@@ -0,0 +1,124 @@
+<?php
+
+namespace Drupal\user\Form;
+
+use Drupal\Core\Entity\ContentEntityForm;
+use Drupal\Core\Entity\EntityConstraintViolationListInterface;
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+ * Form controller for the user account forms.
+ */
+class UserEmailChangeForm extends ContentEntityForm {
+
+  /**
+   * {@inheritdoc}
+   * 
+   * Removing base form id to avoid unapplicable hooks from contrib modules.
+   */
+  public function getBaseFormId() {
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\user\UserInterface $account */
+    $account = $this->entity;
+    $user = $this->currentUser();
+    $config = \Drupal::config('user.settings');
+    $form['#cache']['tags'] = $config->getCacheTags();
+
+    // Account information.
+    $form['account'] = [
+      '#type'   => 'container',
+      '#weight' => -10,
+    ];
+
+    $form['account']['current_pass'] = [
+      '#type' => 'password',
+      '#title' => $this->t('Current password'),
+      '#size' => 25,
+      '#access' => !$form_state->get('user_pass_reset'),
+      '#attributes' => ['autocomplete' => 'off'],
+      '#required' => TRUE,
+    ];
+    $form_state->set('user', $account);
+
+    // The mail field is NOT required if account originally had no mail set
+    // and the user performing the edit has 'administer users' permission.
+    // This allows users without email address to be edited and deleted.
+    // Also see \Drupal\user\Plugin\Validation\Constraint\UserMailRequired.
+    $form['account']['mail'] = [
+      '#type' => 'email',
+      '#title' => $this->t('Email address'),
+      '#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'),
+      '#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
+      '#default_value' => $account->getEmail(),
+    ];
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    $entity = parent::validateForm($form, $form_state);
+    $new_email = $form_state->getValue('mail');
+    $current_email = $form_state->getFormObject()->entity->getEmail();
+    if ($new_email == $current_email) {
+      $form_state->setErrorByName('mail', t("You didn't change your email."));
+    }
+
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildEntity(array $form, FormStateInterface $form_state) {
+    /** @var \Drupal\user\UserInterface $account */
+    $account = parent::buildEntity($form, $form_state);
+
+    // Set existing password if set in the form state.
+    $current_pass = trim($form_state->getValue('current_pass'));
+    if (strlen($current_pass) > 0) {
+      $account->setExistingPassword($current_pass);
+    }
+
+    return $account;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditedFieldNames(FormStateInterface $form_state) {
+    return ['mail'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function flagViolations(EntityConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
+    // Manually flag violations of fields not handled by the form display. This
+    // is necessary as entity form displays only flag violations for fields
+    // contained in the display.
+    $field_names = ['mail'];
+    foreach ($violations->getByFields($field_names) as $violation) {
+      list($field_name) = explode('.', $violation->getPropertyPath(), 2);
+      $form_state->setErrorByName($field_name, $violation->getMessage());
+    }
+    parent::flagViolations($violations, $form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    parent::submitForm($form, $form_state);
+    $this->messenger()->addStatus('Email changed successfully.');
+  }
+
+}
diff --git a/modules/user/src/Form/UserPasswordChangeForm.php b/modules/user/src/Form/UserPasswordChangeForm.php
new file mode 100644
index 0000000000..4416a2ea71
--- /dev/null
+++ b/modules/user/src/Form/UserPasswordChangeForm.php
@@ -0,0 +1,137 @@
+<?php
+
+namespace Drupal\user\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\user\Entity\User;
+use Drupal\Core\Password\PasswordInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Form controller for the user password forms.
+ *
+ * Users followed the link in the email, now they can enter a new password.
+ *
+ * @internal
+ */
+class UserPasswordChangeForm extends FormBase {
+
+  /**
+   * The Password Hasher.
+   *
+   * @var \Drupal\Core\Password\PasswordInterface
+   */
+  protected $passwordHasher;
+
+  /**
+   * Constructs a UserPasswordForm object.
+   *
+   * @param \Drupal\Core\Password $password_hasher
+   *   The password hasher.
+   */
+  public function __construct(PasswordInterface $password_hasher) {
+    $this->passwordHasher = $password_hasher;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('password')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'user_pass_change';
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @param array $form
+   *   An associative array containing the structure of the form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   * @param \Drupal\Core\Session\AccountInterface $user
+   *   User requesting reset.
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, AccountInterface $user = NULL) {
+    $account = $user;
+    $user = $this->currentUser();
+    $config = \Drupal::config('user.settings');
+    $form['#cache']['tags'] = $config->getCacheTags();
+
+    // To skip the current password field, the user must have logged in via a
+    // one-time link and have the token in the URL. Store this in $form_state
+    // so it persists even on subsequent Ajax requests.
+    if (!$form_state->get('user_pass_reset') && ($token = $this->getRequest()->get('pass-reset-token'))) {
+      $session_key = 'pass_reset_' . $account->id();
+      $user_pass_reset = isset($_SESSION[$session_key]) && hash_equals($_SESSION[$session_key], $token);
+      $form_state->set('user_pass_reset', $user_pass_reset);
+    }
+
+    // The user must enter their current password to change to a new one.
+    $form['account']['current_pass'] = [
+      '#type' => 'password',
+      '#title' => $this->t('Current password'),
+      '#size' => 25,
+      '#access' => !$form_state->get('user_pass_reset'),
+      '#attributes' => ['autocomplete' => 'off'],
+      '#required' => TRUE,
+    ];
+    $form_state->set('user', $account);
+
+    // The user may only change their own password without their current
+    // password if they logged in via a one-time login link.
+    if (!$form_state->get('user_pass_reset')) {
+      $form['account']['current_pass']['#description'] = $this->t('Required if you want to change the %pass below. <a href=":request_new_url" title="Send password reset instructions via email.">Reset your password</a>.', [
+        '%pass' => $this->t('Password'),
+        ':request_new_url' => $this->url('user.pass'),
+      ]);
+    }
+
+    $form['account']['pass'] = [
+      '#type' => 'password_confirm',
+      '#required' => TRUE,
+      '#size' => 25,
+      '#description' => $this->t('To change the current user password, enter the new password in both fields.'),
+    ];
+    $form['actions'] = ['#type' => 'actions'];
+    $form['actions']['submit'] = ['#type' => 'submit', '#value' => $this->t('Change Password')];
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+    // Check current pass only if not password reset.
+    if (!$form_state->get('user_pass_reset')) {
+      $current_pass = $form_state->getValue('current_pass');
+      $user = $form_state->get('user');
+      if (!$this->passwordHasher->check($current_pass, $user->getPassword())) {
+        $form_state->setErrorByName('current_pass', t('Your current password is incorrect.'));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    /** @var Drupal\Core\Session\AccountInterface $user */
+    $user = User::load($form_state->get('user')->id());
+    // Set new password.
+    $new_pass = $form_state->getValue('pass');
+    $user->setPassword($new_pass);
+    $user->save();
+    $this->messenger()->addStatus('Password changed successfully.');
+  }
+
+}
diff --git a/modules/user/user.links.task.yml b/modules/user/user.links.task.yml
index 5b0d1648f4..5dc898dde9 100644
--- a/modules/user/user.links.task.yml
+++ b/modules/user/user.links.task.yml
@@ -29,11 +29,29 @@ entity.user.canonical:
   route_name: entity.user.canonical
   base_route: entity.user.canonical
   title: View
-entity.user.edit_form:
+
+entity.user.edit_collection:
   route_name: entity.user.edit_form
   base_route: entity.user.canonical
   title: Edit
 
+entity.user.edit_form:
+  route_name: entity.user.edit_form
+  parent_id: entity.user.edit_collection
+  title: Edit Profile
+
+entity.user.change_pass_form:
+  route_name: user.edit_pass.form
+  parent_id: entity.user.edit_collection
+  title: Change Password
+  weight: 1
+
+entity.user.change_email_form:
+  route_name: entity.user.change_email_form
+  parent_id: entity.user.edit_collection
+  title: Change Email
+  weight: 2
+
 entity.user.collection:
   title: List
   route_name: entity.user.collection
diff --git a/modules/user/user.routing.yml b/modules/user/user.routing.yml
index dc618eed8a..04751ba9a8 100644
--- a/modules/user/user.routing.yml
+++ b/modules/user/user.routing.yml
@@ -216,3 +216,16 @@ user.well-known.change_password:
     _controller: '\Drupal\user\Controller\UserController::userEditPage'
   requirements:
     _user_is_logged_in: 'TRUE'
+
+user.edit_pass.form:
+  path: '/user/{user}/edit-pass'
+  defaults:
+    _form: '\Drupal\user\Form\UserPasswordChangeForm'
+    _title: 'Change password'
+  requirements:
+    user : '\d+'
+    _entity_access : 'user.update'
+  options:
+    _maintenance_access: TRUE
+    _admin_route: TRUE
+    no_cache: TRUE
