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

@suprsend/react-editor

v0.0.4

Published

A React SDK for creating and managing multi-channel notification templates. Provides a complete UI for editing templates across 7 notification channels with a visual email designer, merge tag support, display conditions, live preview, and theming.

Readme

SuprSend React Template Editor

A React SDK for creating and managing multi-channel notification templates. Provides a complete UI for editing templates across 7 notification channels with a visual email designer, merge tag support, display conditions, live preview, and theming.

Table of Contents


Installation

npm install @suprsend/react-editor
# or
yarn add @suprsend/react-editor

Quick Start

import {
  SuprSendTemplateProvider,
  TemplateEditor,
  CommitButton,
  TestButton,
} from '@suprsend/react-editor';
import '@suprsend/react-editor/styles.css';

function App() {
  return (
    <SuprSendTemplateProvider
      workspaceUid="your-workspace-uid"
      templateSlug="order-confirmation"
      variantId="default"
      channels={['email', 'inbox', 'androidpush', 'webpush']}
      tenantId={null}
      locale="en"
      theme="light"
    >
      <TemplateEditor onCommit={() => console.log('Committed!')} />
    </SuprSendTemplateProvider>
  );
}

Important: You must import the stylesheet (@suprsend/react-editor/styles.css) for the components to render correctly.


Supported Channels

| Channel | ID | Features | | ---------------- | ------------- | ------------------------------------------------------------------------------- | | Email | email | Visual designer, raw HTML, plain text, metadata, merge tags, display conditions | | Slack | slack | Block Kit (JSON) or plain text | | MS Teams | ms_teams | Adaptive Cards (JSON) or plain text | | Android Push | androidpush | Title, body, buttons, image, custom payload, silent/sticky options | | iOS Push | iospush | Title, body, image, action URL | | Web Push | webpush | Title, body, buttons, image, action URL | | In-app Inbox | inbox | Rich content, avatar, buttons, tags, expiry, importance levels | | SMS | sms | Coming soon | | WhatsApp | whatsapp | Coming soon |


Components

SuprSendTemplateProvider

The root context provider. Wrap your editor UI with this component.

<SuprSendTemplateProvider
  workspaceUid="ws_123"
  templateSlug="welcome-email"
  variantId="default"
  channels={['email', 'inbox', 'webpush']}
  tenantId={null}
  locale="en"
  theme="dark"
  themeOverrides={{ primary: '#6366f1', radius: '8px' }}
  accessToken="your-access-token"
  refreshAccessToken={async (oldToken) => {
    const newToken = await fetchNewToken(oldToken);
    return newToken;
  }}
>
  {children}
</SuprSendTemplateProvider>

Props

| Prop | Type | Required | Default | Description | | ---------------------- | --------------------------------------- | -------- | --------- | ------------------------------------------------------------ | | workspaceUid | string | Yes | — | Your SuprSend workspace identifier | | templateSlug | string | Yes | — | The template slug | | variantId | string | Yes | — | The variant identifier | | channels | ChannelId[] | Yes | — | Array of channels to enable in the editor | | tenantId | string \| null | Yes | — | Tenant ID for multi-tenant workspaces (pass null if N/A) | | locale | string | Yes | — | Language/locale code (e.g. "en") | | conditions | unknown | No | — | Variant conditions configuration | | accessToken | string | Yes | — | Authentication token | | refreshAccessToken | (oldToken: string) => Promise<string> | No | — | Callback to refresh an expired token | | mode | 'live' \| 'draft' | No | 'draft' | Start in draft (editable) or live (read-only) mode | | theme | 'light' \| 'dark' \| 'system' | No | 'light' | UI theme | | themeOverrides | ThemeOverrides | No | — | Custom color and styling overrides (see Theming) | | recipientDistinctId | string | No | — | Recipient ID for populating mock/preview data | | actorDistinctId | string | No | — | Actor ID for populating mock/preview data | | notificationCategory | string | No | — | Notification category for triggering test |


TemplateEditor

The main editor component. Renders channel tabs, channel editor forms, and preview panes.

<TemplateEditor
  hideActionButtons={false}
  onCommit={() => console.log('Committed!')}
/>

Props

| Prop | Type | Required | Default | Description | | ------------------- | ------------ | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | hideActionButtons | boolean | No | false | Hide the default action buttons (Test, Commit, Edit, Exit) | | hideTestButton | boolean | No | false | Hide only the Test button while keeping other action buttons visible | | onCommit | () => void | No | — | Callback invoked after a successful commit . If you hide action buttons you dont need to pass it as you can directly pass it to commit button component |

Tip: When hideActionButtons is true, you can use the standalone TestButton and CommitButton components to place them anywhere in your layout.


CommitButton

A button that opens the commit modal. Validates all variants, lets the user select which to publish, and requires a commit message.

<CommitButton onCommit={() => console.log('Published!')} />

Props

| Prop | Type | Required | Description | | ---------- | ------------ | -------- | ----------------------------- | | onCommit | () => void | Yes | Callback invoked after commit |


TestButton

A button that opens the test modal. Allows sending a test notification to a real recipient with mock data.

<TestButton onTestSent={() => console.log('Test sent!')} />

Props

| Prop | Type | Required | Description | | ------------ | ------------ | -------- | ----------------------------------- | | onTestSent | () => void | No | Callback invoked after test is sent |


Theming

The editor supports light, dark, and system (auto-detect) themes. Customize the look and feel using themeOverrides.

<SuprSendTemplateProvider
  theme="light"
  themeOverrides={{
    primary: '#6366f1',
    primaryForeground: '#ffffff',
    background: '#fafafa',
    foreground: '#111827',
    border: '#e5e7eb',
    radius: '8px',
  }}
  // ...other props
>

Available Overrides

| Property | Description | | ----------------------- | ---------------------------------------- | | background | Main background color | | foreground | Main text color | | card | Card/panel background | | cardForeground | Card text color | | popover | Popover/dropdown background | | popoverForeground | Popover text color | | primary | Primary brand color | | primaryForeground | Text on primary backgrounds | | secondary | Secondary color | | secondaryForeground | Text on secondary backgrounds | | muted | Muted/subtle backgrounds | | mutedForeground | Muted text color | | accent | Accent highlight color | | accentForeground | Text on accent backgrounds | | destructive | Error/danger color | | destructiveForeground | Text on destructive backgrounds | | border | Border color | | input | Input field border color | | ring | Focus ring color | | radius | Border radius (e.g. "8px", "0.5rem") |

All CSS classes are scoped with a suprsend- prefix to prevent conflicts with your application's styles.


Authentication

Provide an accessToken and optionally a refreshAccessToken callback to handle token expiration.

<SuprSendTemplateProvider
  workspaceUid="ws_123"
  accessToken={token}
  refreshAccessToken={async (oldToken) => {
    const response = await fetch('/api/refresh-token', {
      method: 'POST',
      body: JSON.stringify({ token: oldToken }),
    });
    const { newToken } = await response.json();
    return newToken;
  }}
  // ...other props
/>

When a 401 response is received, the SDK automatically calls refreshAccessToken, queues pending requests, and retries them with the new token.

Generating access token

import time
import jwt
from typing import Dict, Any

# ==================== CONFIGURATION ====================

signing_key_id = "signing_key_xxxxxx"

# Private key in PEM format
signing_private_key = """-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PRIVATE KEY-----"""

# ==================== PAYLOAD ====================

payload: Dict[str, Any] = {
    "workspace_uid": "<ws_uid>",
    "entity_type": "template",
    "entity_id": "<template-slug>",          # Use "*" to allow access to all templates

    "scope": {
        # Template-level scope
        "variant_scope": {                   # variant_scope: If missing, user can access all variants
            "channels": ["email"],           # If missing, any channel can be accessed
            "variant_id": "v1",              # If missing, any variant can be accessed
            "tenant_id": "<tenant1>",        # If missing or null, variants of all tenants can be accessed
            "locale": "en",                  # If missing, variants of all locales can be accessed

            "conditions": [                  # If missing, variants of all conditions can be accessed
                {
                    "expression_v1": {
                        "op": "AND",
                        "args": [
                            {"variable_ns": "", "variable": "age", "op": "==", "value": "18"},
                            {"variable_ns": "", "variable": "age", "op": "==", "value": "18"}
                        ]
                    }
                }
            ],

            "fallback_variant_id": "var_1"   # If the requested variant is not present,
                                             # use content from this fallback variant
        },

        # Only the recipients mentioned below can be accessed
        # (used for mock testing and variable fetching)
        # If this key is missing, mock/variable functionality might not work properly
        "recipients": [
            {"distinct_id": "id1"},
            {"distinct_id": "id2"}
        ]
    },

    # Token expiration settings
    "iat": int(time.time()),                    # Issued at (current time in unix seconds)
    "exp": int(time.time()) + 3600              # Expiration time (add extra seconds as needed)
}

# ==================== JWT HEADER ====================

header_dict = {
    "alg": "ES256",
    "kid": signing_key_id,
    "typ": "JWT"
}

# ==================== GENERATE TOKEN ====================

auth_token = jwt.encode(
    payload=payload,
    key=signing_private_key,
    algorithm="ES256",
    headers=header_dict
)

print(auth_token)