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

@genuine-forms/web-components

v1.1.34

Published

**One-stop form handling + transactional email** with built-in, privacy-first human verification (via **Genuine Captcha**). Drop the component into any HTML page—no framework required.

Downloads

166

Readme

Genuine Forms — HTML5 Web Components

One-stop form handling + transactional email with built-in, privacy-first human verification (via Genuine Captcha). Drop the component into any HTML page—no framework required.

  • ✅ Native Custom Elements: <genuine-form> and <genuine-captcha>
  • ✅ Blocks submission until captcha is verified and required fields are valid
  • ✅ Works with plain HTML or any framework (React, Vue, Svelte, Astro, …)
  • ✅ Zero build-step required (ES module)
  • ✅ Event-driven architecture for easy customization
  • ✅ Shadow DOM with CSS custom properties for styling

Hot Links

Demo / CDN

  • Production script: https://cryptng.github.io/genuine-forms-vanillajs/genuine-form.js

You can load this URL directly in a <script type="module"> tag for quick demos or static sites.


Install

Option A: npm (recommended)

npm install @genuine-forms/web-components

Then import once in your app init script or similar:

// For example for emberjs in app.js
import '@genuine-forms/web-components';

Option B: Script tag (no build tools)

<script type="module" src="https://cryptng.github.io/genuine-forms-vanillajs/genuine-form.js"></script>

Quick start (Plain HTML)

<script type="module" src="https://cryptng.github.io/genuine-forms-vanillajs/genuine-form.js"></script>

<genuine-form subject="New Project Request" api-key="YOUR_API_KEY" name="contact-form">
  <input name="first-name" required /> <br />
  <input name="last-name" required /> <br />
  <input name="email" type="email" required /> <br />
  <textarea name="project" rows="8"></textarea> <br />
  <input name="privacy" type="checkbox" required /> <br />

  <genuine-captcha>
    <button type="submit">Send</button>
  </genuine-captcha>
</genuine-form>

How it works

  • Put your normal HTML inputs inside <genuine-form>.
  • Wrap your submit button with <genuine-captcha>…</genuine-captcha>.
  • Submission is only allowed after captcha verification and HTML validation (required, type="email", etc.) pass.

Component API

<genuine-form …>

Attributes

  • subject (string) — Subject line used for the outgoing email. Default: 'Generic Subject'
  • name (string, optional) — Unique identifier for the form. Required when using multiple genuine-forms on the same page. Default: 'genuine-form'
  • api-key (string, optional) — Your Genuine Forms API key for production use.
  • receiver (string, optional) — For Developer Early Preview only. XOR-encrypted email address. Falls back to api-key if not set.
  • api-url (string, optional) — Custom API endpoint. Default: https://genuine-forms.io/api/gf-send-dev

Slot content

  • Standard form controls: <input>, <select>, <textarea>
  • One <genuine-captcha> wrapper containing a <button type="submit">…</button>

Validation

  • Native HTML5 validation is honored:
    • required on inputs, textareas, and selects
    • type="email" for email validation
    • Required checkboxes must be checked
    • Radio button groups (at least one must be selected)
    • Multi-select dropdowns must have at least one selection

<genuine-captcha>

  • Renders & manages the captcha challenge provided by the Genuine Captcha widget.
  • Blocks the parent form's submission until verified.
  • Automatically imported from https://cryptng.github.io/genuine-captcha-vanillajs/genuine-captcha.js

Event System

Access the event system through window.genuineForms[formName] where formName matches your form's name attribute (default: 'genuine-form').

// Register event handlers
window.genuineForms['contact-form'].on('send-response', (response) => {
  if (response.ok) {
    console.log('Email sent successfully:', response.body);
  } else {
    console.error('Error:', response.error);
  }
});

window.genuineForms['contact-form'].on('validation-failed', () => {
  alert('Please fill out all required fields.');
});

window.genuineForms['contact-form'].on('started-sending', () => {
  console.log('Sending email...');
});

window.genuineForms['contact-form'].on('finished-sending', () => {
  console.log('Send attempt completed.');
});

// Remove event handlers
window.genuineForms['contact-form'].off('send-response');

Available Events

  • send-response — Triggered after form submission completes. Receives { ok: boolean, body?: any, error?: any }
  • validation-failed — Triggered when form validation fails on submit attempt
  • started-sending — Triggered immediately before sending the request
  • finished-sending — Triggered after the request completes (success or failure)

Customization Hooks (optional)

You can customize validation, payload generation, and initialization by defining the following global functions before the component initializes:

<script>
  // Custom validation logic
  // Receives: formName (string), formElement (<genuine-form>)
  // Returns: boolean
  window.genuineFormHandleValidate = (formName, formElement) => {
    // Access form values
    const email = formElement.querySelector('[name="email"]');
    
    // Custom validation
    if (email && !email.value.includes('@')) {
      return false;
    }
    
    // You can still use the default validation
    return formElement.querySelectorAll('[required]').length > 0;
  };

  // Custom subject and body generation
  // Receives: formName (string), formElement (<genuine-form>), defaultSubject (string)
  // Returns: { subject: string, body: string }
  window.genuineFormGenerateSubjectAndBody = (formName, formElement, defaultSubject) => {
    const inputs = formElement.querySelectorAll('[name]');
    const data = {};
    
    inputs.forEach(input => {
      if (input.type === 'checkbox') {
        data[input.name] = input.checked;
      } else if (input.type === 'radio') {
        if (input.checked) data[input.name] = input.value;
      } else {
        data[input.name] = input.value;
      }
    });
    
    return {
      subject: `[${formName}] ${defaultSubject}`,
      body: JSON.stringify(data, null, 2)
    };
  };

  // Initialization callback
  // Receives: formName (string), formElement (<genuine-form>)
  window.genuineFormHandleInitialized = (formName, formElement) => {
    console.log(`Form "${formName}" is ready!`);
    // Perform any setup needed after the component initializes
  };
</script>

Note: The component waits up to 15 seconds for these global functions to be defined. If not found, sensible defaults are used.


Styling

The component uses Shadow DOM with CSS custom properties for easy styling:

genuine-form {
  --form-display: flex;
  --form-flex-direction: column;
  --form-gap: 1rem;
  --form-padding: 1rem;
  --form-border-radius: 0.5rem;
  --form-border: 1px solid #ccc;
  --form-background-color: #fff;
  --form-box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

Style the slotted content (your inputs and buttons) with regular CSS:

genuine-form input {
  padding: 0.5rem;
  border: 1px solid #ddd;
}

genuine-form button[type="submit"] {
  background: #007bff;
  color: white;
  padding: 0.75rem 1.5rem;
  border: none;
  cursor: pointer;
}

Multiple Forms on One Page

When using multiple forms on the same page, provide unique name attributes:

<genuine-form name="contact-form" subject="Contact Request" api-key="YOUR_API_KEY">
  <!-- form fields -->
  <genuine-captcha>
    <button type="submit">Send</button>
  </genuine-captcha>
</genuine-form>

<genuine-form name="newsletter-form" subject="Newsletter Signup" api-key="YOUR_API_KEY">
  <!-- form fields -->
  <genuine-captcha>
    <button type="submit">Subscribe</button>
  </genuine-captcha>
</genuine-form>

<script>
  // Access each form's events independently
  window.genuineForms['contact-form'].on('send-response', (res) => {
    console.log('Contact form response:', res);
  });
  
  window.genuineForms['newsletter-form'].on('send-response', (res) => {
    console.log('Newsletter form response:', res);
  });
</script>

Backend / Delivery

You have two options:

  1. Use the Genuine Forms hosted backend (recommended):

    • Set your api-key attribute
    • The component sends data to https://genuine-forms.io/api/gf-send-dev by default
    • Override with api-url if needed
  2. Bring your own backend:

    • Set a custom api-url attribute pointing to your endpoint
    • Your API should:
      • Accept GET requests with query parameters: captchaSolution, captchaSecret, apiKey, subject, body
      • Verify the captcha token
      • Send the email (e.g., via SMTP or a mail API)
      • Return JSON: { body: "success message" } on success or appropriate error response

Example custom endpoint:

<genuine-form 
  api-url="https://your-api.com/send-email"
  api-key="YOUR_KEY"
  subject="Contact Form">
  <!-- form fields -->
</genuine-form>

Security note: Do not put private secrets in the browser. Keep API secrets on the server.


Framework Notes

  • React/Vue/Svelte/etc.: Custom Elements work out-of-the-box. Use standard HTML attributes (kebab-case).
  • SSR/Static: Works on static hosts; the component upgrades on the client.
  • TypeScript: Type definitions coming soon.

React Example:

function ContactForm() {
  useEffect(() => {
    window.genuineForms['my-form']?.on('send-response', (res) => {
      if (res.ok) {
        alert('Thank you for your message!');
      }
    });
    
    return () => {
      window.genuineForms['my-form']?.off('send-response');
    };
  }, []);

  return (
    <genuine-form name="my-form" subject="Contact" api-key="YOUR_KEY">
      <input name="name" required />
      <input name="email" type="email" required />
      <genuine-captcha>
        <button type="submit">Send</button>
      </genuine-captcha>
    </genuine-form>
  );
}

Accessibility

  • Because you use native inputs, standard a11y applies (labels, focus order, contrast).
  • Always include proper <label> elements for your inputs.
  • Genuine Captcha offers an accessible fallback; ensure it's enabled in your configuration.

Troubleshooting

  • Captcha doesn't appear → Ensure the widget script is loading and not blocked by CSP/AdBlock.
  • Submit button does nothing → Check that:
    • It's inside <genuine-captcha>
    • It has type="submit"
    • The captcha is verified
    • All required fields are filled
  • Validation always fails → Verify all required fields have a name attribute and valid values.
  • Events not firing → Ensure you're accessing the correct form name: window.genuineForms['your-form-name']
  • Multiple submit buttons → If you have multiple forms without unique name attributes, you'll see a console warning. Provide unique names.
  • CORS/Network errors → Confirm your backend endpoint, allowed origins, and JSON response shape.
  • Custom functions not working → Ensure they're defined before the component loads, or within the 15-second initialization window.

Developer Early Preview

The developer early preview lets you try out Genuine Forms without an API key. You need to provide the receiver email address in an encrypted format using XOR encryption.

Encrypt your email:

function xor_encrypt(key, data) {
  return data.split('').map(function(c, i) {
    return c.charCodeAt(0) ^ key.charCodeAt(Math.floor(i % key.length));
  });
}

// Replace with your email and choose a secret key
const encrypted = xor_encrypt("your-secret-key", "[email protected]");
console.log(encrypted.toString());
// Example output: '16,65,20,7,21,12,28,24,0,35,21,10,27,20,3,22,24,17,12,30,93,6,12,31'

Use the encrypted email:

<genuine-form 
  receiver="16,65,20,7,21,12,28,24,0,35,21,10,27,20,3,22,24,17,12,30,93,6,12,31" 
  subject="Contact Request"
  name="preview-form">
  <input name="first-name" required /> <br />
  <input name="last-name" required /> <br />
  <input name="email" type="email" required /> <br />
  <textarea name="message" rows="8"></textarea> <br />
  <input name="privacy" type="checkbox" required /> <br />

  <genuine-captcha>
    <button type="submit">Send</button>
  </genuine-captcha>
</genuine-form>

Important:

  • Don't use the example encrypted string above—create your own
  • Never commit your encryption key or plain email to your repository
  • This is for development/preview only; use api-key for production

Need help? Message via Reddit: https://www.reddit.com/user/love2Bbreath3Dlife/


Roadmap

  • ✅ Event-driven architecture
  • ✅ Shadow DOM with CSS custom properties
  • ✅ Multiple forms support
  • ✅ Custom validation and payload generation
  • ⬜ POST JSON submission option
  • ⬜ TypeScript type definitions
  • ⬜ Comprehensive unit tests
  • ⬜ First-party React/Vue wrappers (optional)
  • ⬜ Enhanced error messages and debugging

API Reference Summary

Form Collection

The component automatically collects form data from:

  • Text inputs → string
  • Checkboxes → boolean
  • Radio buttons → string (selected value)
  • Select (single) → string
  • Select (multiple) → string[]
  • Textareas → string

Validation Rules

Built-in validation checks:

  • Required fields must have values
  • Email inputs must be valid email format
  • Checkboxes with required must be checked
  • Radio groups with required must have one selected
  • Multi-selects with required must have at least one option selected

License

MIT


Contributing

Issues and pull requests welcome! Please ensure any changes maintain backward compatibility and include appropriate documentation updates.


Changelog

Current Version

  • Event-driven architecture with .on() and .off() methods
  • Shadow DOM implementation with CSS custom properties
  • Multiple form support with unique name attributes
  • Custom validation, payload generation, and initialization hooks
  • Support for custom API endpoints via api-url
  • Comprehensive form value collection and validation

See Git tags/releases for detailed version history.