npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cekemail/widget

v1.2.4

Published

Email validation widget for CekEmail - validates email addresses in real-time on your website

Readme

@cekemail/widget

Official email-validation widget for CekEmail — real-time email verification for your forms.

Installation

Get an API key at cekemail.com.

CDN

<script>
  CekEmail_APIKEY = 'wk_xxxxxxxxxxxxx';
</script>
<script src="https://static.cekemail.com/widget/v1/cekemail.min.js"></script>
  • widget/v1/cekemail.min.js serves the latest 1.x release: you get every 1.x fix and feature automatically, but never a breaking 2.x release. Prefer it in production.
  • widget/cekemail.min.js always serves the latest release, including future major releases with breaking changes.

Alternative CDN: the package is also available on jsDelivr at https://cdn.jsdelivr.net/npm/@cekemail/widget@1. The @1 pins the major version the same way v1/ does. To lock an exact release, use a full version such as @cekemail/[email protected].

NPM

For bundlers:

npm install @cekemail/widget

Usage

CDN (Simple Embed)

Add this code to your website, just before the closing </body> tag:

<script>
  CekEmail_APIKEY = 'wk_xxxxxxxxxxxxx';
</script>
<script src="https://static.cekemail.com/widget/v1/cekemail.min.js"></script>

The widget will automatically:

  • Detect all <input type="email"> elements
  • Validate emails when users leave the field (blur)
  • Display visual indicators (✓ for valid, ✗ for invalid)

NPM (ES Module)

import { CekEmail } from '@cekemail/widget';

const widget = new CekEmail();

widget.init({
  apiKey: 'wk_xxxxxxxxxxxxx',
  debounce: 500,           // optional: delay before validation (ms)
  showIndicator: true,     // optional: show ✓/✗ icons
  validateOnBlur: true,    // optional: validate on blur
  validateOnChange: false, // optional: validate while typing
});

CommonJS

const { CekEmail } = require('@cekemail/widget');

const widget = new CekEmail();
widget.init({ apiKey: 'wk_xxxxxxxxxxxxx' });

Configuration

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | null | Widget API key (required) | | apiUrl | string | https://api.cekemail.com/v1/widget/email-check | API endpoint | | debounce | number | 800 | Delay before validation (ms) | | showIndicator | boolean | true | Show validation icons | | showSuggestion | boolean | true | Show the "did you mean" hint under the input | | autoAttach | boolean | true | Auto-attach to email inputs | | validateOnBlur | boolean | true | Validate when field loses focus | | validateOnChange | boolean | false | Validate while typing | | cssClass | object | See below | Custom CSS class names | | locale | string | 'en' | Language for built-in messages (en, id) | | messages | object \| function | null | Custom messages keyed by reason code, or a function (see Localization) |

Self-hosted API

The widget always calls https://api.cekemail.com/v1/widget/email-check, wherever the script itself is loaded from. If you run your own CekEmail API, set the endpoint explicitly with CekEmail_API_URL:

<script>
  CekEmail_APIKEY = 'wk_xxxxxxxxxxxxx';
  CekEmail_API_URL = 'https://email-check.example.com/v1/widget/email-check';
</script>
<script src="https://static.cekemail.com/widget/v1/cekemail.min.js"></script>

With npm, pass apiUrl to init() instead:

widget.init({
  apiKey: 'wk_xxxxxxxxxxxxx',
  apiUrl: 'https://email-check.example.com/v1/widget/email-check',
});

CSS Classes

{
  valid: 'cekemail-valid',
  invalid: 'cekemail-invalid',
  checking: 'cekemail-checking',
  wrapper: 'cekemail-wrapper',
  indicator: 'cekemail-indicator',
  suggestion: 'cekemail-suggestion',
}

Localization

The widget ships with English and Indonesian messages. Pick one with locale, override any string with messages, or supply a function for full control. Messages are shown as the input's tooltip and in the data-cekemail-message attribute.

<script>
  CekEmail_APIKEY = 'wk_xxxxxxxxxxxxx';
  CekEmail_LOCALE = 'id';
  CekEmail_MESSAGES = { mailbox_not_found: 'Alamat ini tidak ditemukan' };
</script>
<script src="https://static.cekemail.com/widget/v1/cekemail.min.js"></script>
widget.init({
  apiKey: 'wk_xxxxxxxxxxxxx',
  locale: 'en',
  messages: {
    invalid_format: 'Please enter a valid email address',
    disposable: 'Temporary email providers are not allowed',
  },
});

// Function form: return a string, or a falsy value to fall back to the defaults
widget.init({
  apiKey: 'wk_xxxxxxxxxxxxx',
  messages: (result, isValid) => (isValid ? null : `We could not verify ${result.input}`),
});

Messages are looked up by the API's reason_code. Keys you can override:

| Key | When it is shown | |-----|------------------| | checking | While a lookup is in progress | | did_you_mean | The API returned a likely correction; :suggestion is the corrected address | | invalid_format | The address failed the local format check | | mailbox_exists | The mailbox exists and accepts mail | | mailbox_not_found | The mail server reported the mailbox does not exist | | mailbox_full | The mailbox is over quota | | mailbox_not_allowed | The mail server does not accept this mailbox name | | no_mx_records | The domain has no mail server | | disposable | The domain is a disposable email provider | | catch_all | The domain accepts any address | | greylisted | The mail server asked for a retry | | smtp_unreachable | No mail server could be reached | | server_temporarily_unavailable | The mail server returned a temporary failure | | sender_blocked | The mail server refused the verification | | policy_rejected | The mail server rejected the address for policy reasons | | unknown | The verification could not be completed | | valid / invalid | Generic fallbacks when no code matches |

Did you mean

When the API returns a suggestion (for example [email protected] for [email protected]), the widget shows a small hint under the input. Clicking the suggested address fills the field and validates it again. Translate it with the did_you_mean message, where :suggestion is replaced by the corrected address.

<script>
  CekEmail_APIKEY = 'wk_xxxxxxxxxxxxx';
  CekEmail_LOCALE = 'id';
  CekEmail_MESSAGES = { did_you_mean: 'Mungkin maksud Anda :suggestion?' };
</script>
<script src="https://static.cekemail.com/widget/v1/cekemail.min.js"></script>

Set showSuggestion: false to keep the suggestion out of the DOM and render your own hint from the cekemail:validated event, which carries result.suggestion.

API

init(config?)

Initialize the widget with optional configuration.

widget.init({
  apiKey: 'wk_xxxxxxxxxxxxx',
  debounce: 500,
});

setApiKey(key)

Replace the API key used for subsequent requests. Inputs stay attached and cached results are kept.

widget.setApiKey('wk_yyyyyyyyyyyyy');

Rotating keys

If your page hands out short-lived keys, the script does not need a key at load time. Load it without CekEmail_APIKEY, then call init() once the first key arrives and setApiKey() whenever it is replaced:

// First key: initialize the widget that was loaded without one
window.CekEmail.init({ apiKey: firstKey });

// Later: rotate without re-initializing
window.CekEmail.setApiKey(nextKey);

Calling setApiKey() before init() makes that key the one init() uses.

validate(input | email)

Validate an input element or email string.

// Validate input element
const input = document.querySelector('#email');
widget.validate(input);

// Validate email string
const result = await widget.validate('[email protected]');
console.log(result.is_valid);

validateEmailDirectly(email)

Validate an email string and return the result.

const result = await widget.validateEmailDirectly('[email protected]');
console.log(result);
// {
//   is_valid: true,
//   is_reachable: true,
//   is_disposable_email: false,
//   reason: 'Email is valid'
// }

attachToInput(input)

Manually attach the widget to an input element.

const input = document.querySelector('#my-email');
widget.attachToInput(input);

clearValidationState(input)

Clear validation state from an input.

widget.clearValidationState(input);

clearCache()

Clear the validation result cache.

widget.clearCache();

getCacheSize()

Get the number of cached validation results.

console.log(widget.getCacheSize());

Events

cekemail:validated

Fired when an email is validated.

document.addEventListener('cekemail:validated', (event) => {
  const { input, result, isValid } = event.detail;

  console.log('Email:', input.value);
  console.log('Valid:', isValid);
  console.log('Result:', result);
});

Disabling on Specific Inputs

Add data-cekemail-disable to prevent validation:

<input type="email" data-cekemail-disable />

CSS Styling

The widget adds classes to inputs based on validation state:

/* Valid email */
input.cekemail-valid {
  border-color: #22c55e !important;
}

/* Invalid email */
input.cekemail-invalid {
  border-color: #ef4444 !important;
}

/* Checking email */
input.cekemail-checking {
  border-color: #3b82f6 !important;
}

Override with your own styles:

input.cekemail-valid {
  border: 2px solid green;
  background-color: #f0fff0;
}

input.cekemail-invalid {
  border: 2px solid red;
  background-color: #fff0f0;
}

TypeScript

Full TypeScript support with exported types:

import {
  CekEmail,
  CekEmailConfig,
  ValidationResult,
  ValidatedEventDetail,
} from '@cekemail/widget';

const config: Partial<CekEmailConfig> = {
  apiKey: 'wk_xxxxxxxxxxxxx',
  debounce: 500,
};

const widget = new CekEmail();
widget.init(config);

// Type-safe validation result
const result: ValidationResult = await widget.validateEmailDirectly('[email protected]');

Browser Support

  • Chrome (last 2 versions)
  • Firefox (last 2 versions)
  • Safari (last 2 versions)
  • Edge (last 2 versions)

Publishing

For maintainers:

npm login
npm publish

npm publish runs the build first (prepublishOnly), and publishConfig.access makes the scoped package public. Bump version in package.json before publishing.

Pushing to main or publishing a GitHub release deploys the build to static.cekemail.com/widget/ and purges the Cloudflare cache for those files.

jsDelivr serves the new npm version within minutes. To refresh the @1 alias right away, purge it:

https://purge.jsdelivr.net/npm/@cekemail/widget@1

Links

License

MIT