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

@prakash.gurung/commerce-newsletter

v1.1.0

Published

Email signup and subscription management for an Adobe Commerce storefront. Supported Node versions are 20 and 22.

Readme

Newsletter drop-in

Email signup and subscription management for an Adobe Commerce storefront. Supported Node versions are 20 and 22.

Every label on the screen comes from the newsletter placeholder sheet. Edit that sheet. Do not add these rows to placeholders/global.

Install and run

npm install

Put the GraphQL endpoint in .env. Storybook reads it.

ENDPOINT="https://your-store.example/graphql"

The sandbox endpoint is set in examples/html-host/index.html.

npm run dev
  • Sandbox: http://127.0.0.1:3000
  • Storybook: http://localhost:6006
npm test
npm run build

Change labels in the newsletter sheet

In the placeholders library, create a placeholder named newsletter. That creates placeholders/newsletter.json, next to placeholders/global.json. Leave global unchanged.

The sheet has two columns, the same as global:

| Key | Value | | --- | --- | | Newsletter.Form.title | Subscribe to our newsletter | | Newsletter.Form.description | Be the first to hear about new products, offers, and stories. | | Newsletter.Form.emailLabel | Email address | | Newsletter.Form.emailPlaceholder | Enter your email address | | Newsletter.Form.submit | Subscribe | | Newsletter.Form.submitting | Subscribing... | | Newsletter.Form.invalidEmail | Enter a valid email address. | | Newsletter.Form.success | Thanks for subscribing. | | Newsletter.Form.error | We couldn't subscribe you. Please try again. | | Newsletter.Minified.title | Newsletter | | Newsletter.Minified.subscribed | Subscribed to the general newsletter. | | Newsletter.Minified.unsubscribed | Not subscribed. | | Newsletter.Minified.edit | Edit | | Newsletter.Subscription.title | Subscription option | | Newsletter.Subscription.general | General Subscription | | Newsletter.Subscription.save | SAVE | | Newsletter.Subscription.saving | Saving... | | Newsletter.Subscription.saved | Subscription saved. | | Newsletter.Subscription.error | We couldn't update your subscription. Please try again. | | Newsletter.Status.loading | Loading subscription... | | Newsletter.Status.loadError | We couldn't load your subscription. Please try again. |

The same rows are in placeholders/newsletter.json in this project. After the newsletter placeholder exists, edit its Value cells. Keys you leave out keep the default.

Email address (Newsletter.Form.emailLabel) is the accessible name of the email field. It is not typed inside the box. The text inside the box is the placeholder, Enter your email address (Newsletter.Form.emailPlaceholder). Change that row in the newsletter sheet when the hint should say something else.

Register the drop-in with that sheet. The published file is /placeholders/newsletter.json.

import { initializers } from '@dropins/tools/initializer.js';
import * as newsletter from '@prakash.gurung/commerce-newsletter/api.js';

initializers.register(newsletter.initialize, {
  placeholderSheet: '/placeholders/newsletter.json',
});

initializers.mount();

Mount the drop-in

import * as newsletter from '@prakash.gurung/commerce-newsletter/api.js';
import { render } from '@prakash.gurung/commerce-newsletter/render.js';
import NewsletterContainer from '@prakash.gurung/commerce-newsletter/containers/NewsletterContainer.js';

newsletter.setEndpoint('https://your-store.example/graphql');

render.render(NewsletterContainer, {
  // see the three views below
})(document.getElementById('newsletter'));

The page block is a table named commerce-newsletter. The Minified view cell chooses the second and third views. Leave that row out for the normal form.

| commerce-newsletter | | | --- | --- | | Minified view | true or false | | Newsletter page | /newsletter |

Newsletter page is the address the Edit link opens. Pass it as newsletterUrl. The default is /newsletter.

Signed-in customers are required for Minified view. The drop-in reads customer.is_subscribed and saves with updateCustomer.

1. Normal form

Use this on a marketing or footer placement. Do not set Minified view.

render.render(NewsletterContainer, {})(document.getElementById('newsletter'));

What the shopper sees: the title, the description, an email field with the placeholder, and Subscribe.

  1. The shopper enters an email address and chooses Subscribe.
  2. An empty or invalid address shows the Newsletter.Form.invalidEmail text.
  3. A valid address is sent with subscribeEmailToNewsletter.
  4. Status SUBSCRIBED or UNCONFIRMED shows the success text and clears the field.
  5. Any other result shows the API message, or Newsletter.Form.error.

A successful subscribe emits newsletter/subscribed with { email, status }.

title, description, and placeholder on this render call replace the sheet for that one mount:

render.render(NewsletterContainer, {
  title: 'Join the list',
  description: 'Weekly notes.',
  placeholder: '[email protected]',
})(document.getElementById('newsletter'));

2. Minified view true

Use this where the account summary should stay short. The shopper sees the subscription state and Edit. Edit opens the newsletter page, which uses Minified view false.

Block table:

| commerce-newsletter | | | --- | --- | | Minified view | true | | Newsletter page | /newsletter |

render.render(NewsletterContainer, {
  minified: true,
  newsletterUrl: '/newsletter',
})(document.getElementById('newsletter-summary'));

minified may be the boolean true or the string "true" from the table cell.

  1. The drop-in loads the signed-in customer's subscription.
  2. Subscribed customers see Newsletter.Minified.subscribed. Everyone else sees Newsletter.Minified.unsubscribed.
  3. Edit (Newsletter.Minified.edit) goes to newsletterUrl.
  4. That page is the checkbox view in the next section.

3. Minified view false

Use this on the newsletter page. The shopper sees Subscription option, the General Subscription checkbox, and SAVE. Checking the box subscribes. Clearing it unsubscribes. Nothing is saved until SAVE.

Block table:

| commerce-newsletter | | | --- | --- | | Minified view | false |

render.render(NewsletterContainer, {
  minified: false,
})(document.getElementById('newsletter'));

minified may be the boolean false or the string "false" from the table cell.

  1. The drop-in loads customer.is_subscribed and checks the box when the customer is already subscribed.
  2. The shopper checks or unchecks General Subscription.
  3. SAVE sends updateCustomer with is_subscribed.
  4. The button reads Newsletter.Subscription.saving while the request runs, then Newsletter.Subscription.saved.
  5. A failed save shows the API message, or Newsletter.Subscription.error.

A successful save emits newsletter/updated with { subscribed }.

Files

| Piece | File | | --- | --- | | Normal email form | src/components/NewsletterForm/NewsletterForm.tsx | | Minified summary and checkbox | src/components/NewsletterSubscription/NewsletterSubscription.tsx | | Which view to show | src/containers/NewsletterContainer/NewsletterContainer.tsx | | Subscribe, load, and save requests | src/api/newsletter/newsletter.ts | | Default copy | src/i18n/en_US.json | | Placeholder merge | src/api/initialize/initialize.ts | | Newsletter label sheet | placeholders/newsletter.json | | Local host page | examples/html-host/index.html |