same-email
v1.0.0
Published
Normalize and compare email addresses — Gmail dots, plus-tags, Yahoo hyphen aliases, and more. Stop duplicate signups.
Downloads
167
Maintainers
Readme
same-email
Normalize and compare email addresses so duplicate signups cannot hide behind Gmail dots, plus-tags, or Yahoo hyphen aliases.
Zero dependencies · TypeScript · Node 18+ · Browsers · ESM + CJS
The problem
Users create multiple accounts with the same inbox:
[email protected]
[email protected]
[email protected]Your database sees three different emails. Gmail sees one inbox.
same-email canonicalizes addresses before insert or lookup.
Install
npm install same-emailyarn add same-email
pnpm add same-emailQuick start
import { canonicalEmail, isSameEmail } from 'same-email';
canonicalEmail(' [email protected] ');
// → '[email protected]'
isSameEmail('[email protected]', '[email protected]');
// → trueAPI
canonicalEmail(email, options?)
Returns a canonical string, or null when the input is invalid.
canonicalEmail('[email protected]'); // '[email protected]'
canonicalEmail('[email protected]'); // '[email protected]'
canonicalEmail('[email protected]'); // '[email protected]'
canonicalEmail('[email protected]'); // '[email protected]'
canonicalEmail('[email protected]'); // '[email protected]' (unknown domain)isSameEmail(left, right, options?)
Compares two addresses after canonicalization.
isSameEmail('[email protected]', '[email protected]'); // true
isSameEmail('[email protected]', '[email protected]'); // falsenormalizeEmail(email, options?)
Alias for canonicalEmail.
parseEmail(email)
Low-level parser. Returns { local, domain } or null.
Provider rules (v1)
| Provider | Dots ignored | Plus-tags stripped | Other |
|----------|--------------|--------------------|-------|
| Gmail / Googlemail | yes | yes | googlemail.com → gmail.com |
| Outlook / Hotmail / Live / MSN | no | yes | |
| Yahoo | no | no | hyphen aliases stripped (john-filter → john) |
| iCloud / me.com / mac.com | no | yes | |
| Proton Mail | yes | yes | |
| Fastmail | no | yes | |
| Other domains | no | no | trim + lowercase only |
Plus-tags are only stripped for providers known to support them — so unknown domains are not incorrectly merged.
Options
canonicalEmail('not-valid', { strict: true }); // throws TypeError
canonicalEmail('not-valid'); // nullUse in auth / signup
import { canonicalEmail } from 'same-email';
async function findExistingUser(rawEmail: string) {
const email = canonicalEmail(rawEmail);
if (!email) throw new Error('Invalid email');
return db.user.findFirst({ where: { canonicalEmail: email } });
}
async function register(rawEmail: string, password: string) {
const email = canonicalEmail(rawEmail);
if (!email) throw new Error('Invalid email');
const exists = await db.user.findFirst({ where: { canonicalEmail: email } });
if (exists) throw new Error('Account already exists');
return db.user.create({
data: {
email: rawEmail.trim().toLowerCase(),
canonicalEmail: email,
passwordHash: await hash(password),
},
});
}Store both:
email— what the user typed (for communication)canonicalEmail— for unique index / duplicate detection
FAQ
Why same-email and not canonical-email?
The name canonical-email is already taken on npm. same-email matches the main use case: “are these two addresses the same inbox?”
Does this validate deliverability?
No. It checks structure and applies provider normalization rules. Use an email verification service for deliverability.
Can I use this in the browser?
Yes. Zero dependencies, no Node-only APIs.
Contributing
Issues and PRs welcome: github.com/sameerzaman15/same-email
License
MIT © Sameer Zaman
