Authentication
Forgot password
A centered card that sends a password reset link and says where it went.
<div class="grid min-h-dvh place-items-center bg-base-200 p-6">
<div class="flex w-full max-w-sm flex-col gap-6">
<a class="flex items-center gap-2 self-center font-medium" href="#">
<span class="grid size-8 place-items-center rounded-lg bg-primary text-primary-content">
<span class="icon icon-stack-2 size-5"></span>
</span>
Pelatform
</a>
<form class="card" id="reset-form" method="post">
<div class="card-header">
<h1 class="card-title text-xl">Reset your password</h1>
<p class="card-description">Enter the email you sign in with, and a link to reset your password goes to it.</p>
</div>
<div class="card-body">
<div class="field">
<label class="field-label" for="reset-email-input">Email</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-mail"></span>
</span>
<input class="input-group-control" id="reset-email-input" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
</div>
<div class="card-actions flex-col">
<button class="btn btn-primary w-full">
<span class="btn-label">Send reset link</span>
<span class="btn-loading">
<span class="btn-icon btn-icon-loading"></span>
Sending…
</span>
</button>
<a class="btn btn-ghost w-full" href="#">Back to sign in</a>
</div>
</form>
<div class="card" id="reset-sent" hidden>
<div class="card-header">
<h1 class="card-title text-xl" tabindex="-1">Check your email</h1>
<p class="card-description">A reset link is on its way to <strong id="reset-address">john@example.com</strong>. It expires in 30 minutes.</p>
</div>
<div class="card-actions flex-col">
<button class="btn btn-outline w-full" id="reset-again">
<span class="btn-label">Resend link</span>
<span class="btn-loading">
<span class="btn-icon btn-icon-loading"></span>
Sending…
</span>
</button>
<a class="btn btn-ghost w-full" href="#">Back to sign in</a>
</div>
</div>
</div>
</div>
<script>
const form = document.getElementById('reset-form');
const sent = document.getElementById('reset-sent');
// Your request; the button shows its loading part until it settles.
const send = button => PLF.Button.loading(button, fetch('/password-reset', { method: 'POST', body: new FormData(form) }));
form.addEventListener('submit', async event => {
event.preventDefault();
await send(event.submitter);
document.getElementById('reset-address').textContent = form.elements.email.value;
form.hidden = true;
sent.hidden = false;
sent.querySelector('h1').focus();
});
document.getElementById('reset-again').addEventListener('click', event => send(event.currentTarget));
</script><div class="plf:grid plf:min-h-dvh plf:place-items-center plf:bg-base-200 plf:p-6">
<div class="plf:flex plf:w-full plf:max-w-sm plf:flex-col plf:gap-6">
<a class="plf:flex plf:items-center plf:gap-2 plf:self-center plf:font-medium" href="#">
<span class="plf:grid plf:size-8 plf:place-items-center plf:rounded-lg plf:bg-primary plf:text-primary-content">
<span class="plf:icon plf:icon-stack-2 plf:size-5"></span>
</span>
Pelatform
</a>
<form class="plf:card" id="reset-form" method="post">
<div class="plf:card-header">
<h1 class="plf:card-title plf:text-xl">Reset your password</h1>
<p class="plf:card-description">Enter the email you sign in with, and a link to reset your password goes to it.</p>
</div>
<div class="plf:card-body">
<div class="plf:field">
<label class="plf:field-label" for="reset-email-input">Email</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-mail"></span>
</span>
<input class="plf:input-group-control" id="reset-email-input" name="email" type="email" dir="ltr" autocomplete="email" placeholder="name@example.com" required>
</div>
</div>
</div>
<div class="plf:card-actions plf:flex-col">
<button class="plf:btn plf:btn-primary plf:w-full">
<span class="plf:btn-label">Send reset link</span>
<span class="plf:btn-loading">
<span class="plf:btn-icon plf:btn-icon-loading"></span>
Sending…
</span>
</button>
<a class="plf:btn plf:btn-ghost plf:w-full" href="#">Back to sign in</a>
</div>
</form>
<div class="plf:card" id="reset-sent" hidden>
<div class="plf:card-header">
<h1 class="plf:card-title plf:text-xl" tabindex="-1">Check your email</h1>
<p class="plf:card-description">A reset link is on its way to <strong id="reset-address">john@example.com</strong>. It expires in 30 minutes.</p>
</div>
<div class="plf:card-actions plf:flex-col">
<button class="plf:btn plf:btn-outline plf:w-full" id="reset-again">
<span class="plf:btn-label">Resend link</span>
<span class="plf:btn-loading">
<span class="plf:btn-icon plf:btn-icon-loading"></span>
Sending…
</span>
</button>
<a class="plf:btn plf:btn-ghost plf:w-full" href="#">Back to sign in</a>
</div>
</div>
</div>
</div>
<script>
const form = document.getElementById('reset-form');
const sent = document.getElementById('reset-sent');
// Your request; the button shows its loading part until it settles.
const send = button => PLF.Button.loading(button, fetch('/password-reset', { method: 'POST', body: new FormData(form) }));
form.addEventListener('submit', async event => {
event.preventDefault();
await send(event.submitter);
document.getElementById('reset-address').textContent = form.elements.email.value;
form.hidden = true;
sent.hidden = false;
sent.querySelector('h1').focus();
});
document.getElementById('reset-again').addEventListener('click', event => send(event.currentTarget));
</script>'use client';
import { useEffect, useRef, useState, type FormEvent } from 'react';
import {
Button, buttonClass, Card, CardActions, CardBody, CardDescription, CardHeader, Field, Icon, InputGroup,
InputGroupAddon, InputGroupControl,
} from '@pelatform/react';
export function Example() {
const form = useRef<HTMLFormElement>(null);
const email = useRef<HTMLInputElement>(null);
const title = useRef<HTMLHeadingElement>(null);
const [sending, setSending] = useState(false);
// The address the link went to, once it is sent; the second card then takes the form's place.
const [sentTo, setSentTo] = useState<string>();
// The second card's title takes the focus once the card shows.
useEffect(() => {
if (sentTo !== undefined) title.current?.focus();
}, [sentTo]);
// Your request; the button shows its loading part until it settles.
async function send() {
setSending(true);
try {
await fetch('/password-reset', { method: 'POST', body: new FormData(form.current!) });
} finally {
setSending(false);
}
}
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
await send();
setSentTo(email.current!.value);
}
return (
<div className="grid min-h-dvh place-items-center bg-base-200 p-6">
<div className="flex w-full max-w-sm flex-col gap-6">
<a className="flex items-center gap-2 self-center font-medium" href="#">
<span className="grid size-8 place-items-center rounded-lg bg-primary text-primary-content">
<Icon name="stack-2" className="size-5" />
</span>
Pelatform
</a>
<form className="card" ref={form} method="post" hidden={sentTo !== undefined} onSubmit={submit}>
<CardHeader>
<h1 className="card-title text-xl">Reset your password</h1>
<CardDescription>Enter the email you sign in with, and a link to reset your password goes to it.</CardDescription>
</CardHeader>
<CardBody>
<Field label="Email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl ref={email} name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
</CardBody>
<CardActions className="flex-col">
<Button color="primary" className="w-full" loading={sending} loadingText="Sending…">
Send reset link
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} w-full`} href="#">Back to sign in</a>
</CardActions>
</form>
<Card hidden={sentTo === undefined}>
<CardHeader>
<h1 className="card-title text-xl" ref={title} tabIndex={-1}>Check your email</h1>
<CardDescription>A reset link is on its way to <strong>{sentTo}</strong>. It expires in 30 minutes.</CardDescription>
</CardHeader>
<CardActions className="flex-col">
<Button variant="outline" className="w-full" loading={sending} loadingText="Sending…" onClick={send}>
Resend link
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} w-full`} href="#">Back to sign in</a>
</CardActions>
</Card>
</div>
</div>
);
}'use client';
import { useEffect, useRef, useState, type FormEvent } from 'react';
import {
Button, buttonClass, Card, CardActions, CardBody, CardDescription, CardHeader, Field, Icon, InputGroup,
InputGroupAddon, InputGroupControl,
} from '@pelatform/react/prefixed';
export function Example() {
const form = useRef<HTMLFormElement>(null);
const email = useRef<HTMLInputElement>(null);
const title = useRef<HTMLHeadingElement>(null);
const [sending, setSending] = useState(false);
// The address the link went to, once it is sent; the second card then takes the form's place.
const [sentTo, setSentTo] = useState<string>();
// The second card's title takes the focus once the card shows.
useEffect(() => {
if (sentTo !== undefined) title.current?.focus();
}, [sentTo]);
// Your request; the button shows its loading part until it settles.
async function send() {
setSending(true);
try {
await fetch('/password-reset', { method: 'POST', body: new FormData(form.current!) });
} finally {
setSending(false);
}
}
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
await send();
setSentTo(email.current!.value);
}
return (
<div className="plf:grid plf:min-h-dvh plf:place-items-center plf:bg-base-200 plf:p-6">
<div className="plf:flex plf:w-full plf:max-w-sm plf:flex-col plf:gap-6">
<a className="plf:flex plf:items-center plf:gap-2 plf:self-center plf:font-medium" href="#">
<span className="plf:grid plf:size-8 plf:place-items-center plf:rounded-lg plf:bg-primary plf:text-primary-content">
<Icon name="stack-2" className="plf:size-5" />
</span>
Pelatform
</a>
<form className="plf:card" ref={form} method="post" hidden={sentTo !== undefined} onSubmit={submit}>
<CardHeader>
<h1 className="plf:card-title plf:text-xl">Reset your password</h1>
<CardDescription>Enter the email you sign in with, and a link to reset your password goes to it.</CardDescription>
</CardHeader>
<CardBody>
<Field label="Email">
<InputGroup>
<InputGroupAddon>
<Icon name="mail" />
</InputGroupAddon>
<InputGroupControl ref={email} name="email" type="email" dir="ltr" autoComplete="email" placeholder="name@example.com" required />
</InputGroup>
</Field>
</CardBody>
<CardActions className="plf:flex-col">
<Button color="primary" className="plf:w-full" loading={sending} loadingText="Sending…">
Send reset link
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} plf:w-full`} href="#">Back to sign in</a>
</CardActions>
</form>
<Card hidden={sentTo === undefined}>
<CardHeader>
<h1 className="plf:card-title plf:text-xl" ref={title} tabIndex={-1}>Check your email</h1>
<CardDescription>A reset link is on its way to <strong>{sentTo}</strong>. It expires in 30 minutes.</CardDescription>
</CardHeader>
<CardActions className="plf:flex-col">
<Button variant="outline" className="plf:w-full" loading={sending} loadingText="Sending…" onClick={send}>
Resend link
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} plf:w-full`} href="#">Back to sign in</a>
</CardActions>
</Card>
</div>
</div>
);
}<link rel="stylesheet" href="pelatform-ui.min.css">
<link rel="stylesheet" href="icons/icons.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.min.css">
<link rel="stylesheet" href="components/button.min.css">
<link rel="stylesheet" href="components/form.min.css">
<link rel="stylesheet" href="components/card.min.css">
<link rel="stylesheet" href="icons/icons.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/button.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script><link rel="stylesheet" href="pelatform-ui.prefixed.min.css">
<link rel="stylesheet" href="icons/icons.prefixed.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.prefixed.min.css">
<link rel="stylesheet" href="components/button.prefixed.min.css">
<link rel="stylesheet" href="components/form.prefixed.min.css">
<link rel="stylesheet" href="components/card.prefixed.min.css">
<link rel="stylesheet" href="icons/icons.prefixed.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/button.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script>