Home

Awesome

React Accounts UI

Forked from https://github.com/studiointeract/accounts-ui to address outstanding issues.

Install this package by doing: meteor add epotek:accounts-ui.

Here's how to quickly get started:

import React from 'react';
import { Accounts } from 'meteor/epotek:accounts-ui';
import { T9n } from 'meteor-accounts-t9n';
import { en } from 'meteor-accounts-t9n/build/en'; // Choose the language you need here

T9n.map('en', en);
T9n.setLanguage('en');

const App = () => (
  <div>
    <Accounts.ui.LoginForm />
  </div>
);

export default App;

Features

  1. Easy to use, mixing the ideas of useraccounts configuration and accounts-ui that everyone already knows and loves.
  2. Components are everywhere, and extensible by replacing them on Accounts.ui.
  3. Basic routing included, redirections when the user clicks a link in an email or when signing in or out.
  4. Unstyled is the default, no CSS included.
  5. No password sign up and sign in are included.
  6. Extra fields is now supported.
  7. Extending to make your own custom form, for your app, or as a package, all components can be extended and customized.
  8. States API makes it possible to use the form on different routes, say you want the login on one route and signup on another, just set the inital state and the links (either globally or per component by using the props).
  9. React Router is fully supported, see the example how to use with React Router.
  10. FlowRouter is fully supported, see the example how to use with FlowRouter.
  11. Server Side Rendering is easily setup, see how it's done with FlowRouter (SSR). An example for React Router using react-router-ssr coming shortly.

Styling

This package does not by standard come with any styling, you can easily extend and make your own, here are a couple versions we've made for the typical use case:

Installation

meteor add epotek:accounts-ui
meteor npm install --save meteor-accounts-t9n

Configuration

We support the standard configuration in the account-ui package. But have extended with some new options.

Accounts.ui.config(options)

import { Accounts } from 'meteor/epotek:accounts-ui'

Configure the behavior of <Accounts.ui.LoginForm />

Example configuration:

Accounts.config({
  sendVerificationEmail: true,
  forbidClientAccountCreation: false
});

Accounts.ui.config({
  passwordSignupFields: 'EMAIL_ONLY',
  loginPath: '/login',
  signUpPath: '/signup',
  resetPasswordPath: '/reset-password',
  profilePath: '/profile',
  onSignedInHook: () => FlowRouter.go('/general'),
  onSignedOutHook: () => FlowRouter.go('/login'),
  minimumPasswordLength: 6
});

Version 1.2 also supports passing hooks through props to the component.

import { Accounts } from 'meteor/epotek:accounts-ui';

<Accounts.ui.LoginForm onSignedInHook={() => console.log('user signed in')} />;

Options:

No password required

This package provides a state that makes it possible to create and manage accounts without a password. The idea is simple, you always verify your email, so to login you enter your mail and the system emails you a link to login. The mail that is sent can be changed if needed, just how you alter the email templates in accounts-base.

This is the default setting for passwordSignupFields in the configuration.

Using React Accounts UI

Example setup (Meteor 1.3)

meteor add accounts-password
meteor add std:accounts-ui

import React from 'react';
import { Accounts } from 'meteor/std:accounts-ui';

Accounts.ui.config({
  passwordSignupFields: 'EMAIL_ONLY_NO_PASSWORD',
  loginPath: '/'
});

if (Meteor.isClient) {
  ReactDOM.render(<Accounts.ui.LoginForm />, document.body);
}

Example setup using React Router (Meteor 1.3)

Following the Application Structure from the Meteor Guide.

npm i --save react react-dom react-router
meteor add accounts-password
meteor add std:accounts-ui

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Accounts, STATES } from 'meteor/std:accounts-ui';

import { App } from '../../ui/layouts/app.jsx';
import { Index } from '../../ui/components/index.jsx';

import { Hello } from '../../ui/pages/hello.jsx';
import { Admin } from '../../ui/pages/admin.jsx';
import { NotFound } from '../../ui/pages/not-found.jsx';

Meteor.startup(() => {
  render(
    <Router history={browserHistory}>
      <Route path="/" component={App}>
        <IndexRoute component={Index} />
        <Route path="/signin" component={() => <Accounts.ui.LoginForm />} />
        <Route
          path="/signup"
          component={() => <Accounts.ui.LoginForm formState={STATES.SIGN_UP} />}
        />
        <Route path="/hello/:name" component={Hello} />
      </Route>
      <Route path="/admin" component={App}>
        <IndexRoute component={Admin} />
      </Route>
      <Route path="*" component={NotFound} />
    </Router>,
    document.getElementById('react-root')
  );
});

You can learn more about the remaining components here in the tutorial on React Router Basics by the Meteor Chef.

Example setup using FlowRouter (Meteor 1.3)

npm i --save react react-dom meteor add accounts-password
meteor add std:accounts-ui
meteor add kadira:flow-router-ssr

import React from 'react';
import { Accounts } from 'meteor/std:accounts-ui';
import { FlowRouter } from 'meteor/kadira:flow-router-ssr';

Accounts.ui.config({
  passwordSignupFields: 'EMAIL_ONLY_NO_PASSWORD',
  loginPath: '/login',
  onSignedInHook: () => FlowRouter.go('/general'),
  onSignedOutHook: () => FlowRouter.go('/')
});

FlowRouter.route('/login', {
  action(params) {
    mount(MainLayout, {
      content: <Accounts.ui.LoginForm />
    });
  }
});

Example setup using the STATES api.

You can define the inital state you want in your route for the component, as set the path where the links in the component link to, for example below we have one route for /login and one for /signup.

meteor add accounts-password
meteor add std:accounts-ui
meteor add softwarerero:accounts-t9n
meteor add kadira:flow-router-ssr

import React from 'react';
import { Accounts, STATES } from 'meteor/std:accounts-ui';
import { T9n } from 'meteor-accounts-t9n';

T9n.setLanguage('en');

Accounts.config({
  sendVerificationEmail: true,
  forbidClientAccountCreation: false
});

Accounts.ui.config({
  passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL',
  loginPath: '/login'
});

FlowRouter.route('/login', {
  action(params) {
    mount(MainLayout, {
      content: (
        <Accounts.ui.LoginForm
          {...{
            signUpPath: '/signup'
          }}
        />
      )
    });
  }
});

FlowRouter.route('/signup', {
  action(params) {
    mount(MainLayout, {
      content: (
        <Accounts.ui.LoginForm
          {...{
            formState: STATES.SIGN_UP,
            loginPath: '/login'
          }}
        />
      )
    });
  }
});

Create your own styled version

To you who are a package author, its easy to write extensions for std:accounts-ui by importing and export like the following example:

// package.js

Package.describe({
  name: 'author:accounts-bootstrap',
  version: '1.0.0',
  summary: 'Bootstrap – Accounts UI for React in Meteor 1.3',
  git: 'https://github.com/author/accounts-bootstrap',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.3');
  api.use('ecmascript');
  api.use('std:accounts-ui');

  api.imply('session');

  api.mainModule('main.jsx');
});
// package.json

{
  "name": "accounts-bootstrap",
  "description": "Bootstrap – Accounts UI for React in Meteor 1.3",
  "repository": {
    "type": "git",
    "url": "https://github.com/author/accounts-bootstrap.git"
  },
  "keywords": [
    "react",
    "meteor",
    "accounts",
    "tracker"
  ],
  "author": "author",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/author/accounts-bootstrap/issues"
  },
  "homepage": "https://github.com/author/accounts-bootstrap",
  "dependencies": {
    "react": "^15.x",
    "react-dom": "^15.x",
    "tracker-component": "^1.3.13"
  }
}

To install the dependencies added in your package.json run:
npm i

// main.jsx

import React from 'react';
import PropTypes from 'prop-types';
import { Accounts, STATES } from 'meteor/std:accounts-ui';

/**
 * Form.propTypes = {
 *   fields: PropTypes.object.isRequired,
 *   buttons: PropTypes.object.isRequired,
 *   error: PropTypes.string,
 *   ready: PropTypes.bool
 * };
 */
class Form extends Accounts.ui.Form {
  render() {
    const { fields, buttons, error, message, ready = true } = this.props;
    return (
      <form
        className={ready ? 'ready' : null}
        onSubmit={evt => evt.preventDefault()}
        className="accounts-ui"
      >
        <Accounts.ui.Fields fields={fields} />
        <Accounts.ui.Buttons buttons={buttons} />
        <Accounts.ui.FormMessage message={message} />
      </form>
    );
  }
}

class Buttons extends Accounts.ui.Buttons {}
class Button extends Accounts.ui.Button {}
class Fields extends Accounts.ui.Fields {}
class Field extends Accounts.ui.Field {}
class FormMessage extends Accounts.ui.FormMessage {}
// Notice! Accounts.ui.LoginForm manages all state logic
// at the moment, so avoid overwriting this one, but have
// a look at it and learn how it works. And pull
// requests altering how that works are welcome.

// Alter provided default unstyled UI.
Accounts.ui.Form = Form;
Accounts.ui.Buttons = Buttons;
Accounts.ui.Button = Button;
Accounts.ui.Fields = Fields;
Accounts.ui.Field = Field;
Accounts.ui.FormMessage = FormMessage;

// Export the themed version.
export { Accounts, STATES };
export default Accounts;

Available components

Extra fields

Example provided by @radzom.

import { Accounts, STATES } from 'meteor/std:accounts-ui';

class NewLogin extends Accounts.ui.LoginForm {
  fields() {
    const { formState } = this.state;
    if (formState == STATES.SIGN_UP) {
      return {
        firstname: {
          id: 'firstname',
          hint: 'Enter firstname',
          label: 'firstname',
          onChange: this.handleChange.bind(this, 'firstname')
        },
        ...super.fields()
      };
    }
    return super.fields();
  }

  translate(text) {
    // Here you specify your own translation function, e.g.
    return this.props.t(text);
  }

  signUp(options = {}) {
    const { firstname = null } = this.state;
    if (firstname !== null) {
      options.profile = Object.assign(options.profile || {}, {
        firstname: firstname
      });
    }
    super.signUp(options);
  }
}

And on the server you can store the extra fields like this:

import { Accounts } from 'meteor/accounts-base';

Accounts.onCreateUser(function(options, user) {
  user.profile = options.profile || {};
  user.roles = {};
  return user;
});

Deprecations

v1.2.11

Credits

Made by the creative folks at Studio Interact and all the wonderful people using and improving this package.