---
title: "JavaScript Login"
description: "The LemonadeJS Login block for JavaScript: Multi-screen authentication: login, register, forgot and reset password. Contract-verified, framework-agnostic, zero dependencies, with a live example."
source: https://lemonadejs.com/docs/plugins/login/
---

<link rel="stylesheet" href="/v6/login.css">

# JavaScript Login

`@lemonadejs/login` · ✓ 36 contract checks · framework-agnostic · zero dependencies

`<Login />` — multi-screen authentication block, ported from the v5
plugin with behavioral parity. Seven screens on one endpoint:

  login     username + sha512(password) + remember
  register  profile { company, name, login, username, terms, phone }
  forgot    { username, recovery: 1 } → code screen on success
  code      { h: sha512(code) } (6 digits) → reset screen on success
  reset     { h, password: sha512(password) } (repeat must match)
  bind      server action 'bindSocialAccount': previous payload + password
  terms     server action 'acceptTermsAndConditions': payload + terms

Protocol (v5): POST to url (default: current pathname), credentials
included, device token appended as ?token=. Response { success: 1 }
proceeds — server may answer action: 'resetPassword' (+hash) to force
the reset screen, or data: <base64 png> to demand a captcha (the
captcha input appears and every following request carries `captcha`).
Without onsuccess, the block redirects to result.url || pathname
(after 3s when there is a message to read — v5 timing).

v5 → v6 mapping: google + google-client-id merged into google (the
client id IS the switch; same for microsoft); require-company/phone/
username/terms → company/phone/username/terms; setTerms() → termstext;
jSuites.notification → inline lm-login-message/lm-login-alert;
onupdate (broken in v5 — referenced an undefined variable) →
onchangescreen(screen). Email persists in localStorage('username'),
?create opens register, ?h=`<hash>` opens reset — all v5 behaviors.

## Example

<!--example-->

```js
import { html } from 'lemonadejs';
import Login from '@lemonadejs/login';

// A fake backend so every screen can be tried without a server:
// any email works and the password is "demo" (the block posts its sha512).
const DEMO = '26c669cd0814ac40e5328752b21c4aa6450d16295e4eec30356a06a911c23983'
    + 'aaebe12d5da38eeebfc1b213be650498df8419194d5a26c7e0a50af156853c79';
const realFetch = window.fetch.bind(window);
window.fetch = async (url, init) => {
    if (!String(url).startsWith('/auth')) return realFetch(url, init);
    const data = JSON.parse(init.body || '{}');
    const ok = data.recovery || data.h || data.name !== undefined || data.password === DEMO;
    const body = ok ? { success: 1, message: 'Welcome back!' } : { success: 0, message: 'Wrong password (try "demo")' };
    return new Response(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' } });
};

const App = (props, { state }) => {
    const status = state('Any email, password "demo"');

    return html`<div style="max-width:380px">
        <${Login} url="/auth" remember profile
            onsuccess="${(r) => (status.value = r.message)}"
            onerror="${(r) => (status.value = r.message)}"
            onchangescreen="${(s) => (status.value = 'Screen: ' + s)}" />
        <p>${status}</p>
    </div>`;
};
```

## Installation

```bash
npm install @lemonadejs/login
```

```js
import Login from '@lemonadejs/login';
import '@lemonadejs/login/style.css';
```

Three deployment forms, one component:

```js
html`<${Login} />`                       // by value (no registration)
setComponents({ Login });               // then <Login /> by name anywhere
createWebComponent(Login);              // <lm-login> in plain HTML/any framework
```

## Props

Every declared prop arrives as a **live state** — pass a value for a snapshot or a
state for a two-way live wire. Attribute strings are coerced to the declared type.

| Prop | Type | Default | Description |
|---|---|---|---|
| `url` | string | `''` | endpoint (v5: url; default = current pathname) |
| `device` | string | `''` | device token, appended as ?token= (v5) |
| `logo` | string | `''` | logo image url (v5) |
| `fullscreen` | boolean | `false` | cover the viewport (v5) |
| `google` | string | `''` | Google client id — truthy shows the button (v5: google + google-client-id) |
| `facebook` | boolean | `false` | show the Facebook button (FB SDK carries its own app id) |
| `microsoft` | string | `''` | Microsoft client id — truthy shows the button (v5: microsoft + microsoft-client-id) |
| `remember` | boolean | `false` | offer "remember me" (v5: visibility AND initial checked) |
| `profile` | boolean | `false` | offer the "create a new profile" link (v5) |
| `company` | boolean | `false` | registration collects company (v5: require-company) |
| `phone` | boolean | `false` | registration collects phone (v5: require-phone) |
| `username` | boolean | `false` | registration collects username (v5: require-username) |
| `terms` | boolean | `false` | registration requires terms acceptance (v5: require-terms) |
| `termstext` | string | `''` | custom terms label, trusted HTML (v5: setTerms) |

## Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

- `onload` — after mount (v5)
- `onsuccess` — (result, data) — replaces the redirect (v5)
- `onerror` — (result) — server refusals and network failures (v5)
- `onbeforesend` — (data) — mutate the payload before POST (v5)
- `onbeforecreate` — (profile) — before register/social create (v5)
- `onchangescreen` — (screen) — replaces v5's broken onupdate

## API

```js
import { ref } from 'lemonadejs';
const login = ref();
html`<${Login} ref="${login}" />`;
// login.current.show(...)
```

- `show()`

## Styling

All classes follow the `lm-login-*` convention; visual variants are `data-*`
attributes on the root. Override freely — there is no styling engine to fight.

## Contract

The machine-readable schema ships with the package:

```js
import contract from '@lemonadejs/login/contract.json';
```

`verify.json` carries the conformance proof produced by `verify(Login)`.

Looking for the v5 plugin? See the [archived v5 documentation](/docs/v5/plugins/).