Skip to main content
The @suprsend/react-editor package provides the UI for embeddable templates: channel editors, preview, test, commit, and theming. You can use this to let your customers edit their notification templates or for your internal team to manage templates and run campaigns within your product. Import the CSS bundle for correct layout and styling.

Installation

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

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_uid = "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)

Components

SuprSendTemplateProvider

The root context provider. Wrap your editor UI with this component. This is where you configure which template and variant to edit, which channels to enable, theming, and authentication.
<SuprSendTemplateProvider
  workspaceUid="ws_123"
  templateSlug="welcome-email"
  variantId="default"
  channels={['email', 'inbox', 'webpush']}
  tenantId={null}
  locale="en"
  themeOverrides={{ primary: '#6366f1', radius: '8px' }}
  accessToken="your-access-token"
  refreshAccessToken={async (oldToken) => {
    const newToken = await fetchNewToken(oldToken);
    return newToken;
  }}
>
  {children}
</SuprSendTemplateProvider>
Props
PropTypeDescriptionRequiredDefault
workspaceUidstringYour SuprSend workspace identifier. You can find it on workspace settingsYes
templateSlugstringThe template slugYes
variantIdstringThe variant identifierYes
channelsChannelId[]Array of channels to enable in the editorYes
tenantIdstring | nullTenant ID for multi-tenant workspaces (pass null if N/A)Yes
localestringLanguage/locale code (for example "en")Yes
conditionsConditionExpr[]Audience rules that select which variant to load (see Conditions)No
accessTokenstringAuthentication tokenYes
refreshAccessToken(oldToken: string) => Promise<string>Callback to refresh an expired tokenNo
mode'live' | 'draft'Start in draft (editable) or live (read-only) modeNo'draft'
theme-Coming soonNo-
themeOverridesThemeOverridesCustom color and styling overrides (see Theming)No-
recipientDistinctIdstringRecipient ID for populating mock/preview dataNo-
actorDistinctIdstringActor ID for populating mock/preview dataNo-
notificationCategorystringNotification category for triggering testNo-

Conditions

Use the conditions prop when you want to specify the variant based on conditions other than tenantId and locale. If your variant is identified solely by tenantId and locale, you don’t need this prop. Conditions can be evaluated against the input payload, tenant, actor, and recipient properties to decide which variant applies. Passing conditions scopes the editor to the variant matching those rules. The shape follows the variant_condition_expr_v1_def definition in the variant schema. conditions is an array of expression objects, where each object has:
FieldTypeDescription
typestringAlways "expression_v1"
expression_v1objectThe condition group. Has an op (always "AND") and an args array of clauses
Each clause in args is a single comparison:
FieldTypeRequiredDescription
variablestringYesThe property on the left-hand side of the comparison
variable_nsstring | nullNoNamespace of the variable — "" for the input payload, or one of "$tenant", "$actor", "$recipient"
opstringYesComparison operator (see below)
valuestringCond.Right-hand side value. Required for all operators except EMPTY and NON_EMPTY
All clauses inside a single expression_v1.args are combined with AND. The conditions array itself can hold multiple expression objects to express alternative (OR) audience groups.
Supported operators (op)
OperatorDescriptionData Types
== / !=Equal to / not equal to (case sensitive)All
> / >=Greater than / greater than or equal toNumbers, timestamps
< / <=Less than / less than or equal toNumbers, timestamps
CONTAINS / NOT_CONTAINSSubstring or array item match / no substring or array item matchStrings, arrays
EMPTY / NON_EMPTYKey is missing, empty or null / key is present, not empty or nullAll
ARRAY_INTERSECTS / NOT_ARRAY_INTERSECTSAny array value matches / no array values matchArrays
DATETIME_EQUALS / DATETIME_LT / DATETIME_GTDatetime equal to / before / afterTimestamps
If a key’s data type doesn’t match the operator (for example, using > on a string), the condition always evaluates to false.
Example — each object in the conditions array is OR-ed, while the clauses inside one expression_v1.args are AND-ed. The variant below loads when either the order is high-priority for a Pro-plan tenant OR the acting user is an admin sending to a recipient in India:
<SuprSendTemplateProvider
  workspaceUid="ws_123"
  templateSlug="order-update"
  variantId="v1"
  channels={['email']}
  tenantId={null}
  locale="en"
  accessToken="your-access-token"
  conditions={[
    // Group 1 — high-priority order (input payload) from a Pro-plan tenant
    {
      type: 'expression_v1',
      expression_v1: {
        op: 'AND',
        args: [
          { variable_ns: '', variable: 'priority', op: '==', value: 'high' },
          { variable_ns: '$tenant', variable: 'plan', op: '==', value: 'pro' },
        ],
      },
    },
    // OR Group 2 — admin actor sending to a recipient in India
    {
      type: 'expression_v1',
      expression_v1: {
        op: 'AND',
        args: [
          { variable_ns: '$actor', variable: 'role', op: '==', value: 'admin' },
          { variable_ns: '$recipient', variable: 'country', op: '==', value: 'IN' },
        ],
      },
    },
  ]}
>
  {children}
</SuprSendTemplateProvider>

Supported channels

ChannelIDEditor Functionality
Emailemail3 types of editor available: Visual designer, raw HTML, plain text. Users can switch between editors and see live preview while editing.
Slackslack2 types of editor available: Plain text for designing simple text based notifications and Block Kit (JSON) for advanced interactive messages.
MS Teamsms_teams2 types of editor available: Markdown for designing simple text based notifications and Adaptive Cards (JSON) for advanced interactive messages.
Android PushandroidpushSingle Form editor with rich text messaging support (title, body, image, buttons).
iOS PushiospushSingle Form editor with rich text messaging support (title, body, image, action URL).
Web PushwebpushSingle Form editor with support to add title, body, image, buttons.
In-app InboxinboxSingle Form editor with support for rich content, avatar, buttons, tags, expiry and Pinning.
SMSsmsComing soon
WhatsAppwhatsappComing soon

TemplateEditor

The main editor component. Renders channel tabs, channel editor forms, and preview panes.
<TemplateEditor
  hideActionButtons={false}
  onCommit={() => console.log('Committed!')}
/>
Props
PropTypeRequiredDefaultDescription
hideActionButtonsbooleanNofalseHide the default action buttons (Test, Commit, Edit, Exit)
hideTestButtonbooleanNofalseHide only the Test button while keeping other action buttons visible
onCommit() => voidNo-Callback invoked after a successful commit. If you hide action buttons, you can pass your handler to the standalone CommitButton instead.
When hideActionButtons is true, use the standalone TestButton and CommitButton components to place actions anywhere in your layout.
For self-hosted deployments, pass host="<your-management-api-service-url>".

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
PropTypeRequiredDescription
onCommit() => voidYesCallback 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
PropTypeRequiredDescription
onTestSent() => voidNoCallback invoked after test is sent

Theming

The theme prop (light, dark, system) is coming soon. Customize the look and feel today using themeOverrides.
<SuprSendTemplateProvider
  themeOverrides={{
    primary: '#6366f1',
    primaryForeground: '#ffffff',
    background: '#fafafa',
    foreground: '#111827',
    border: '#e5e7eb',
    radius: '8px',
  }}
  // ...other props
>
  {children}
</SuprSendTemplateProvider>

Available overrides

PropertyDescription
backgroundMain background color
foregroundMain text color
cardCard/panel background
cardForegroundCard text color
popoverPopover/dropdown background
popoverForegroundPopover text color
primaryPrimary brand color
primaryForegroundText on primary backgrounds
secondarySecondary color
secondaryForegroundText on secondary backgrounds
mutedMuted/subtle backgrounds
mutedForegroundMuted text color
accentAccent highlight color
accentForegroundText on accent backgrounds
destructiveError/danger color
destructiveForegroundText on destructive backgrounds
borderBorder color
inputInput field border color
ringFocus ring color
radiusBorder radius (for example "8px", "0.5rem")
All CSS classes are scoped with a suprsend- prefix to prevent conflicts with your application’s styles.