Front-End User Accounts

If your site needs front-end user accounts, follow these steps to get everything up and running.

Step 1: Upgrade to Craft Pro #

First you will need to upgrade to Craft Pro, if you’re not already running that edition, as only Craft Pro is capable of managing multiple user accounts.

Step 2: Check your User Settings #

Go to SettingsUsersSettings, and tick the Allow public registration checkbox, which gives Craft the OK to accept public registration form submissions.

While you’re there, make sure the other user settings look good.

  • We highly recommend you keep the Verify email addresses checkbox ticked, to ensure that all of your users actually have access to the email addresses they’ve set on their accounts.
  • If you want a manual account approval flow, tick the Suspend users by default checkbox.
  • It’s a good idea to select a default user group – one with no permissions associated with it – so you can keep track of the publicly-registered users.

If you want your front-end users to have user photos (avatars), make sure the volume selected in the User Photo Location setting is configured with its Assets in this volume have public URLs setting enabled.

Step 3: Create the Account Management Forms #

Front-end user accounts should not have access to the Craft control panel, so you will need to create your own custom account management forms.

These are traditionally implemented as Twig templates, but you could send Ajax requests to the actions instead if you have a decoupled front end.

Login Form #

Create a login.twig template with a form that posts to the users/login controller action:

<form method="post" accept-charset="UTF-8">
  {{ csrfInput() }}
  {{ actionInput('users/login') }}

  <label for="loginName">Username or email</label>
  {{ input('text', 'loginName', craft.app.user.rememberedUsername, {
    id: 'loginName',
  }) }}

  <label for="password">Password</label>
  {{ input('password', 'password', null, {
    id: 'password',
  }) }}

  <label>
    {{ input('checkbox', 'rememberMe', '1') }}
    Remember me
  </label>

  <button type="submit">Login</button>

  {% if errorMessage is defined %}
    <p>{{ errorMessage }}</p>
  {% endif %}
</form>

<p><a href="{{ url('reset-password') }}">Forgot your password?</a></p>

If you’d prefer to save this template somewhere else, be sure to update your loginPath config setting with the correct path.

You can customize where users should get redirected to after login via the postLoginRedirect config setting.

Registration Form #

Create a register.twig template with a form that posts to the users/save-user controller action:

{% macro errorList(errors) %}
  {% if errors %}
    {{ ul(errors, {class: 'errors'}) }}
  {% endif %}
{% endmacro %}

<form method="post" accept-charset="UTF-8">
  {{ csrfInput() }}
  {{ actionInput('users/save-user') }}
  {{ redirectInput('') }}

  <label for="username">Username</label>
  {{ input('text', 'username', user.username ?? null, {
    id: 'username',
  }) }}

  {% if user is defined %}
    {{ _self.errorList(user.getErrors('username')) }}
  {% endif %}

  <label for="firstName">First Name</label>
  {{ input('text', 'firstName', user.firstName ?? null, {
      id: 'firstName',
  }) }}

  {% if user is defined %}
    {{ _self.errorList(user.getErrors('firstName')) }}
  {% endif %}

  <label for="lastName">Last Name</label>
  {{ input('text', 'lastName', user.lastName ?? null, {
      id: 'lastName',
  }) }}

  {% if user is defined %}
    {{ _self.errorList(user.getErrors('lastName')) }}
  {% endif %}

  <label for="email">Email</label>
  {{ input('email', 'email', user.email ?? null, {
    id: 'email',
  }) }}

  {% if user is defined %}
    {{ _self.errorList(user.getErrors('email')) }}
  {% endif %}

  <label for="password">Password</label>
  {{ input('password', 'password', null, {
    id: 'password',
  }) }}

  {% if user is defined %}
    {{ _self.errorList(user.getErrors('password')) }}
  {% endif %}

  <button type="submit">Register</button>
</form>

If you’ve enabled the useEmailAsUsername setting, you can skip the username field.

Reset Password Forms #

Create a reset-password.twig template with a form that posts to the users/send-password-reset-email controller action:

<form method="post" accept-charset="UTF-8">
  {{ csrfInput() }}
  {{ actionInput('users/send-password-reset-email') }}
  {{ redirectInput('') }}

  <label for="loginName">Username or email</label>
  {{ input('text', 'loginName', loginName ?? craft.app.user.rememberedUsername, {
    id: 'loginName',
  }) }}

  {% if errors ?? false %}
    {{ ul(errors, {class: 'errors'}) }}
  {% endif %}

  <button type="submit">Submit</button>
</form>

You can make this form discoverable by clients that support .well-known/change-password URLs by setting the setPasswordRequestPath config setting to 'reset-password' (or whatever path you actually end up saving this template at).

When this form is submitted with a valid username or email address, Craft will send an email to the user with a link they can click to choose a new password.

You can customize the password-reset email subject and body from UtilitiesSystem MessagesWhen someone forgets their password.

Craft provides a default template for entering a new password, but you can provide a custom one by creating a setpassword.twig template with a form that posts to the users/set-password form:

<form method="post" accept-charset="UTF-8">
  {{ csrfInput() }}
  {{ actionInput('users/set-password') }}
  {{ hiddenInput('code', code) }}
  {{ hiddenInput('id', id) }}

  <label for="new-password">New Password</label>
  {{ input('password', 'newPassword', null, {
    id: 'new-password',
  }) }}

  {% if errors ?? false %}
    {{ ul(errors, {class: 'errors'}) }}
  {% endif %}

  <button type="submit">Submit</button>
</form>

If you’d prefer to save this template somewhere else, be sure to update your setPasswordPath config setting with the correct path.