Authentication
Reset password
A centered card that sets a new password, the page a reset link opens.
<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="new-password-form" method="post">
<div class="card-header">
<h1 class="card-title text-xl">Set a new password</h1>
<p class="card-description">For <strong>john@example.com</strong>.</p>
</div>
<div class="card-body flex flex-col gap-4">
<div class="alert alert-error" id="new-password-expired" role="alert" hidden>
<div class="alert-body">
<div class="alert-content">
<h2 class="alert-title">That link has expired</h2>
<p class="alert-description">
<a class="link" href="#">Send yourself a new one.</a>
</p>
</div>
</div>
</div>
<div class="field">
<label class="field-label" for="new-password">New password</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-key"></span>
</span>
<input class="input-group-control" id="new-password" name="password" type="password" autocomplete="new-password" required minlength="12" aria-describedby="new-password-help">
<button class="input-group-action input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="field-help" id="new-password-help">At least 12 characters.</p>
</div>
<div class="field">
<label class="field-label" for="new-password-confirm">Confirm password</label>
<div class="input-group">
<span class="input-group-addon">
<span class="icon icon-key"></span>
</span>
<input class="input-group-control" id="new-password-confirm" name="confirm" type="password" autocomplete="new-password" required aria-errormessage="new-password-confirm-error">
<button class="input-group-action input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="field-error" id="new-password-confirm-error" hidden></p>
</div>
</div>
<div class="card-actions flex-col">
<button class="btn btn-primary w-full">
<span class="btn-label">Reset password</span>
<span class="btn-loading">
<span class="btn-icon btn-icon-loading"></span>
Saving…
</span>
</button>
<a class="btn btn-ghost w-full" href="#">Back to sign in</a>
</div>
</form>
</div>
</div>
<script>
const form = document.getElementById('new-password-form');
const confirm = form.elements.confirm;
form.addEventListener('submit', async event => {
event.preventDefault();
if (confirm.value !== form.elements.password.value) {
PLF.Field.error(confirm, 'Error: the passwords do not match.');
confirm.focus();
return;
}
PLF.Field.error(confirm, '');
// Your request, with the token from the reset link; the button shows its loading part until it settles.
const body = new FormData(form);
body.append('token', new URLSearchParams(location.search).get('token') ?? '');
const response = await PLF.Button.loading(event.submitter, fetch('/password-reset/confirm', { method: 'POST', body }));
document.getElementById('new-password-expired').hidden = response.ok;
// Otherwise the password is set: continue to sign in.
});
</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="new-password-form" method="post">
<div class="plf:card-header">
<h1 class="plf:card-title plf:text-xl">Set a new password</h1>
<p class="plf:card-description">For <strong>john@example.com</strong>.</p>
</div>
<div class="plf:card-body plf:flex plf:flex-col plf:gap-4">
<div class="plf:alert plf:alert-error" id="new-password-expired" role="alert" hidden>
<div class="plf:alert-body">
<div class="plf:alert-content">
<h2 class="plf:alert-title">That link has expired</h2>
<p class="plf:alert-description">
<a class="plf:link" href="#">Send yourself a new one.</a>
</p>
</div>
</div>
</div>
<div class="plf:field">
<label class="plf:field-label" for="new-password">New password</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-key"></span>
</span>
<input class="plf:input-group-control" id="new-password" name="password" type="password" autocomplete="new-password" required minlength="12" aria-describedby="new-password-help">
<button class="plf:input-group-action plf:input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="plf:field-help" id="new-password-help">At least 12 characters.</p>
</div>
<div class="plf:field">
<label class="plf:field-label" for="new-password-confirm">Confirm password</label>
<div class="plf:input-group">
<span class="plf:input-group-addon">
<span class="plf:icon plf:icon-key"></span>
</span>
<input class="plf:input-group-control" id="new-password-confirm" name="confirm" type="password" autocomplete="new-password" required aria-errormessage="new-password-confirm-error">
<button class="plf:input-group-action plf:input-group-addon" type="button" data-plf-password-toggle hidden></button>
</div>
<p class="plf:field-error" id="new-password-confirm-error" hidden></p>
</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">Reset password</span>
<span class="plf:btn-loading">
<span class="plf:btn-icon plf:btn-icon-loading"></span>
Saving…
</span>
</button>
<a class="plf:btn plf:btn-ghost plf:w-full" href="#">Back to sign in</a>
</div>
</form>
</div>
</div>
<script>
const form = document.getElementById('new-password-form');
const confirm = form.elements.confirm;
form.addEventListener('submit', async event => {
event.preventDefault();
if (confirm.value !== form.elements.password.value) {
PLF.Field.error(confirm, 'Error: the passwords do not match.');
confirm.focus();
return;
}
PLF.Field.error(confirm, '');
// Your request, with the token from the reset link; the button shows its loading part until it settles.
const body = new FormData(form);
body.append('token', new URLSearchParams(location.search).get('token') ?? '');
const response = await PLF.Button.loading(event.submitter, fetch('/password-reset/confirm', { method: 'POST', body }));
document.getElementById('new-password-expired').hidden = response.ok;
// Otherwise the password is set: continue to sign in.
});
</script>'use client';
import { useRef, useState, type FormEvent } from 'react';
import {
Alert, AlertBody, AlertContent, AlertDescription, AlertTitle, Button, buttonClass, CardActions, CardBody,
CardDescription, CardHeader, Field, Icon, InputGroup, InputGroupAddon, InputGroupControl, InputGroupPasswordToggle,
Link,
} from '@pelatform/react';
export function Example() {
const form = useRef<HTMLFormElement>(null);
const confirm = useRef<HTMLInputElement>(null);
const [error, setError] = useState('');
const [saving, setSaving] = useState(false);
const [expired, setExpired] = useState(false);
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(form.current!);
if (data.get('confirm') !== data.get('password')) {
setError('Error: the passwords do not match.');
confirm.current!.focus();
return;
}
setError('');
// Your request, with the token from the reset link; the button shows its loading part until it settles.
data.append('token', new URLSearchParams(location.search).get('token') ?? '');
setSaving(true);
try {
const response = await fetch('/password-reset/confirm', { method: 'POST', body: data });
setExpired(!response.ok);
// Otherwise the password is set: continue to sign in.
} finally {
setSaving(false);
}
}
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" onSubmit={submit}>
<CardHeader>
<h1 className="card-title text-xl">Set a new password</h1>
<CardDescription>For <strong>john@example.com</strong>.</CardDescription>
</CardHeader>
<CardBody className="flex flex-col gap-4">
<Alert color="error" role="alert" hidden={!expired}>
<AlertBody>
<AlertContent>
<AlertTitle level={2}>That link has expired</AlertTitle>
<AlertDescription>
<Link href="#">Send yourself a new one.</Link>
</AlertDescription>
</AlertContent>
</AlertBody>
</Alert>
<Field label="New password" help="At least 12 characters.">
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl name="password" type="password" autoComplete="new-password" required minLength={12} />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
<Field label="Confirm password" error={error}>
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl ref={confirm} name="confirm" type="password" autoComplete="new-password" required />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
</CardBody>
<CardActions className="flex-col">
<Button color="primary" className="w-full" loading={saving} loadingText="Saving…">
Reset password
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} w-full`} href="#">Back to sign in</a>
</CardActions>
</form>
</div>
</div>
);
}'use client';
import { useRef, useState, type FormEvent } from 'react';
import {
Alert, AlertBody, AlertContent, AlertDescription, AlertTitle, Button, buttonClass, CardActions, CardBody,
CardDescription, CardHeader, Field, Icon, InputGroup, InputGroupAddon, InputGroupControl, InputGroupPasswordToggle,
Link,
} from '@pelatform/react/prefixed';
export function Example() {
const form = useRef<HTMLFormElement>(null);
const confirm = useRef<HTMLInputElement>(null);
const [error, setError] = useState('');
const [saving, setSaving] = useState(false);
const [expired, setExpired] = useState(false);
async function submit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const data = new FormData(form.current!);
if (data.get('confirm') !== data.get('password')) {
setError('Error: the passwords do not match.');
confirm.current!.focus();
return;
}
setError('');
// Your request, with the token from the reset link; the button shows its loading part until it settles.
data.append('token', new URLSearchParams(location.search).get('token') ?? '');
setSaving(true);
try {
const response = await fetch('/password-reset/confirm', { method: 'POST', body: data });
setExpired(!response.ok);
// Otherwise the password is set: continue to sign in.
} finally {
setSaving(false);
}
}
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" onSubmit={submit}>
<CardHeader>
<h1 className="plf:card-title plf:text-xl">Set a new password</h1>
<CardDescription>For <strong>john@example.com</strong>.</CardDescription>
</CardHeader>
<CardBody className="plf:flex plf:flex-col plf:gap-4">
<Alert color="error" role="alert" hidden={!expired}>
<AlertBody>
<AlertContent>
<AlertTitle level={2}>That link has expired</AlertTitle>
<AlertDescription>
<Link href="#">Send yourself a new one.</Link>
</AlertDescription>
</AlertContent>
</AlertBody>
</Alert>
<Field label="New password" help="At least 12 characters.">
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl name="password" type="password" autoComplete="new-password" required minLength={12} />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
<Field label="Confirm password" error={error}>
<InputGroup>
<InputGroupAddon>
<Icon name="key" />
</InputGroupAddon>
<InputGroupControl ref={confirm} name="confirm" type="password" autoComplete="new-password" required />
<InputGroupPasswordToggle addon />
</InputGroup>
</Field>
</CardBody>
<CardActions className="plf:flex-col">
<Button color="primary" className="plf:w-full" loading={saving} loadingText="Saving…">
Reset password
</Button>
<a className={`${buttonClass({ variant: 'ghost' })} plf:w-full`} href="#">Back to sign in</a>
</CardActions>
</form>
</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="components/alert.min.css">
<link rel="stylesheet" href="components/link.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 src="components/input-group.min.js" defer></script>
<script src="components/field.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="components/alert.prefixed.min.css">
<link rel="stylesheet" href="components/link.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 src="components/input-group.min.js" defer></script>
<script src="components/field.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script>