Meteor comes with a default user accounts system that is easy to setup. But what if you want something custom?

In this tutorial we will create a simple custom user accounts system. The full code can be found on Github.

Initial Setup

First we need to install Meteor. If you are on OS X or Linux then type the following into your terminal:

curl https://install.meteor.com/ | sh

If you’re on Windows, follow the official Installing Meteor instructions.

Then we will create our app. In your terminal type:

meteor create custom-user-accounts

This creates a new folder called custom-user-accounts with the following files:

custom-user-accounts.js
custom-user-accounts.html
custom-user-accounts.css
.meteor

To run the newly created app:

cd customer-user-accounts
meteor

Open your web browser and go to http://localhost:3000 to see the app running.

Install Meteor default user accounts

Let’s install the two Meteor packages we’ll need—accounts-password and accounts-ui—to setup the default user accounts interface.

meteor add accounts-password
meteor add accounts-ui

The packages are installed, but to display them in our app, we need to add them to our template. Update custom-user-accounts.html to match the following:

# custom-user-accounts.html
<head>
  <title>custom-user-accounts</title>
</head>

<body>
  {{> loginButtons}}
</body>

<template name="hello">
  <button>Click Me</button>
  <p>You've pressed the button {{counter}} times.</p>
</template>

If you navigate to http://127.0.0.1:3000/ the default user accounts interface is setup.

Create custom accounts interface

The nice default interface we have currently is courtesy of accounts-ui, which we will now remove.

meteor remove accounts-ui

However we are NOT removing accounts-password, which provides the backend functionality we will hook into with our custom interface.

The first step is to create signup and login templates.

# customer-user-accounts.html
<head>
  <title>Custom User Accounts Tutorial</title>
</head>

<body>
</body>

<template name="signup">
  <h2>Signup</h2>
  <form>
    <input type="email" name="signupEmail" placeholder="Email">
    <input type="password" name="signupPassword" placeholder="Password">
    <input type="submit" value="Signup">
  </form>
</template>

<template name="login">
  <h2>Log in</h2>
  <form>
    <input type="email" name="loginEmail" placeholder="Email">
    <input type="password" name="loginPassword" placeholder="Password">
    <input type="submit" value="Login">
  </form>
</template>

The signup and login templates are very similar. They each contain a form, email field, password field, and then a submit button. The only difference is the name attribute for the input fields.

But if we head over to http://127.0.0.1:3000/ we see a blank page! Why?

While we have created two new templates, we have not specified where on the page they should go. Here’s how we add templates to our page:

# customer-user-accounts.html
<body>
  {{#if currentUser}}
    <p>You're logged in!</p>
  {{else}}
    {{> signup}}
    {{> login}}
  {{/if}}
</body>

Note that we are also taking advantage of the currentUser object, which Meteor provides, to distinguish between a logged in and logged out view.

If we now navigate over to http://127.0.0.1:3000/ you can see our signup and login forms:

Creating events

Our current signup and login forms are static, which means nothing happens when you click on the Signup or Login buttons. We need to link them to our database.

First we’ll link the submit event for the signup button. Update custom-user-accounts.js so it matches the following:

# custom-user-accounts.js
if (Meteor.isClient) {
  Template.signup.events({
    'submit form': function(event) {
      event.preventDefault();
    }
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

}

We’ve now setup the “signup” template so that default behavior is turned off and it responds to the submit event.

Next we will grab the values from the email and password fields and store them in variables.

Template.signup.events({
  'submit form': function(event) {
    event.preventDefault();
    var emailVar = event.target.signupEmail.value;
    var passwordVar = event.target.signupPassword.value;
  }
});

}

Our “signup” form is now connected but we need to add the “login” form too so we can test that everything works.

Template.login.events({
  'submit form': function(event) {
    event.preventDefault();
    var emailVar = event.target.loginEmail.value;
    var passwordVar = event.target.loginPassword.value;
  }
});

Hooking up things

The accounts-password package makes a number of methods available to us, including Accounts.createUser(), which will we use here.

(By the way, the full Meteor Docs is the go-to resource to check anything Meteor related.)

Add the following to customer-user-accounts.js:

Accounts.createUser({
    email: emailVar,
    password: passwordVar
  });

To enable login, we will use Meteor.loginWithPassword(), which accepts the email and password values. The final code thus looks like this:

Meteor.loginWithPassword(emailVar, passwordVar);

Our custom-user-accounts.js file should now look like the following:

# custom-user-accounts.js
if (Meteor.isClient) {
  Template.signup.events({
    'submit form': function(event) {
      event.preventDefault();
      var emailVar = event.target.signupEmail.value;
      var passwordVar = event.target.signupPassword.value;
      Accounts.createUser({
        email: emailVar,
        password: passwordVar
      });
    }
  });
  Template.login.events({
    'submit form': function(event) {
      event.preventDefault();
      var emailVar = event.target.loginEmail.value;
      var passwordVar = event.target.loginPassword.value;
      Meteor.loginWithPassword(emailVar, passwordVar);
    }
  });
}

if (Meteor.isServer) {
  Meteor.startup(function () {
    // code to run on server at startup
  });
}

Logging Out

We can now signup and log in, so let’s enable logout. First make a new “settings” template that we’ll show when users are logged in.

# customer-user-accounts.html
<template name="settings">
  <p>You're logged in!</p>
  <p><a href="#" class="logout">Logout</a></p>
</template>

And we’ll add this “settings” template to our {{#if currentUser}} statement from earlier:

customer-user-accounts.html

{{#if currentUser}} {{> settings}} {{else}} {{> signup}} {{> login}} {{/if}}

Finally we will take advantage of the built-in Meteor.logout() method:

# custom-user-accounts.js
Template.settings.events({
    'click .logout': function(event) {
      event.preventDefault();
      Meteor.logout();
    }
  });

If we go to http://127.0.0.1:3000/, we should see the following after a successful signup or login:

To see our final working code, check out the Github repo.

Next steps

We have added user signup, login, and logout. But really we should also add email verification and the ability for users to change their password.

The process is similar to the above. Create new templates for each action and link the events.

Good luck!