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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@marvalt/madapter

v2.3.4

Published

MarVAlt adapter for Mautic integration (React + static data generator)

Readme

madapter

A MarVAlt adapter for Mautic marketing automation in React, with static data generation support.

Features

  • 🚀 Mautic API Client - Full-featured client for Mautic API operations
  • 📝 Dynamic Form Rendering - Render Mautic forms dynamically in React
  • 📊 Static Data Generation - Generate static JSON files for build-time data
  • 🎯 Event Tracking - Mautic tracking script integration with proxy support
  • 🔧 React Hooks - Comprehensive React Query hooks for all Mautic operations
  • 🛡️ TypeScript Support - Full TypeScript support with comprehensive type definitions
  • 🔐 Authentication Modes - Support for Cloudflare Worker proxy and direct API access
  • Form Validation - Client-side validation with customizable rules
  • 🎨 Customizable Components - Flexible and customizable React components

Installation

npm install @marvalt/madapter

Peer Dependencies

npm install @tanstack/react-query react react-dom

Quick Start

1. Environment Configuration

Use the same auth mode variable used by all integrations: VITE_AUTH_MODE.

Create .env (non‑secret):

# Global auth mode used across integrations
VITE_AUTH_MODE=cloudflare_proxy # or direct (dev only)

# Mautic via Cloudflare Worker proxy
VITE_MAUTIC_PROXY_URL=https://your-mautic-proxy.workers.dev

# Frontend app identity used by the Worker for auth headers
VITE_FRONTEND_ID=your-app-id
VITE_FRONTEND_SECRET=your-worker-secret

# Mautic OAuth client credentials (typically configured on the Worker)
# Add here only if your tooling expects them documented in the app env.
VITE_MAUTIC_API_PUBLIC_KEY=your-mautic-api-public-key

Create .env.local (secrets for local dev only):

# Secret OAuth key (usually a Worker secret, optional in app env)
VITE_MAUTIC_API_SECRET_KEY=your-mautic-api-secret-key

Notes:

  • In proxy mode, the Worker reads MAUTIC_API_PUBLIC_KEY and MAUTIC_API_SECRET_KEY from its own environment.
  • The frontend sends x-app-id and x-worker-secret headers; the client sets these automatically.

2. React Query Setup

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      {/* Your app components */}
    </QueryClientProvider>
  );
}

The client auto‑configures from import.meta.env/process.env. If you prefer explicit configuration, wrap your app with the provider exported by the package and pass the config fields shown above.

3. Form Rendering (recommended)

import { MauticForm } from '@marvalt/madapter';

function ContactPage() {
  return (
    <div>
      <h1>Contact Us</h1>
      <MauticForm
        formId="1"
        title="Get in Touch"
        description="Send us a message and we'll get back to you soon."
        onSuccess={(data) => console.log('Form submitted:', data)}
        onError={(error) => console.error('Form error:', error)}
      />
    </div>
  );
}

4. Event Tracking

import { MauticTracking } from '@marvalt/madapter';

function App() {
  return (
    <MauticTracking
      enabled={true}
      proxyUrl={process.env.VITE_MAUTIC_PROXY_URL}
      appId={process.env.VITE_FRONTEND_ID}
      workerSecret={process.env.VITE_FRONTEND_SECRET}
    >
      {/* Your app content */}
    </MauticTracking>
  );
}

Static Data Generation

1. Create Generation Script

// scripts/generateMauticData.ts
import { generateMauticData } from '@marvalt/madapter';

async function generateData() {
  try {
    const staticData = await generateMauticData({
      authMode: 'cloudflare_proxy',
      cloudflareWorkerUrl: process.env.VITE_MAUTIC_PROXY_URL,
      appId: process.env.VITE_FRONTEND_ID,
      workerSecret: process.env.VITE_FRONTEND_SECRET,
      outputPath: './src/data/mautic-data.json',
      includeInactive: false,
    });

    console.log(`Generated data for ${staticData.total_forms} forms`);
  } catch (error) {
    console.error('Error generating Mautic data:', error);
    process.exit(1);
  }
}

generateData();

2. Update package.json

{
  "scripts": {
    "generate:mautic-data": "tsx scripts/generateMauticData.ts",
    "build": "npm run generate:mautic-data && vite build"
  }
}

React Hooks

Form Submission

import { useMauticFormSubmission } from '@marvalt/madapter';

function CustomForm() {
  const submitMutation = useMauticFormSubmission();

  const handleSubmit = async (formData) => {
    try {
      await submitMutation.mutateAsync({
        formId: 1,
        fields: formData,
        returnUrl: window.location.href,
      });
      console.log('Form submitted successfully!');
    } catch (error) {
      console.error('Form submission failed:', error);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Your form fields */}
    </form>
  );
}

Contact Management

import { useMauticCreateContact, useMauticContactByEmail } from '@marvalt/madapter';

function ContactManager() {
  const createMutation = useMauticCreateContact();
  const { data: contact } = useMauticContactByEmail('[email protected]');

  const handleCreateContact = async (contactData) => {
    await createMutation.mutateAsync({
      email: contactData.email,
      firstname: contactData.firstName,
      lastname: contactData.lastName,
    });
  };

  return (
    <div>
      {contact && <p>Contact found: {contact.data?.firstname}</p>}
      {/* Your contact management UI */}
    </div>
  );
}

Event Tracking

import { useMauticEventTracking } from '@marvalt/madapter';

function ProductPage() {
  const trackEvent = useMauticEventTracking();

  const handleProductView = () => {
    trackEvent.mutate({
      email: '[email protected]',
      eventName: 'product_view',
      eventData: { productId: '123', category: 'electronics' },
    });
  };

  return (
    <div>
      <button onClick={handleProductView}>Track Product View</button>
    </div>
  );
}

API Reference

MauticClient

import { MauticClient } from '@marvalt/madapter';

const client = new MauticClient({
  authMode: 'cloudflare_proxy',
  cloudflareWorkerUrl: 'https://proxy.workers.dev',
  appId: 'your-app-id',
  workerSecret: 'your-secret',
});

// Form operations
await client.submitForm(1, { formId: 1, fields: { email: '[email protected]' } });
await client.getForms();
await client.getForm(1);

// Contact operations
await client.createContact({ email: '[email protected]', firstname: 'Test' });
await client.updateContact(1, { firstname: 'Updated' });
await client.getContactByEmail('[email protected]');

// Event tracking
await client.trackEvent('page_view', { page: '/home' });

// Tag management
await client.addTagToContact(1, ['newsletter', 'premium']);
await client.removeTagFromContact(1, ['newsletter']);

Configuration

interface MauticConfig {
  authMode: 'cloudflare_proxy' | 'direct';
  apiUrl?: string;                    // Required for direct mode
  cloudflareWorkerUrl?: string;       // Required for proxy mode
  appId?: string;                     // Required for proxy mode
  workerSecret?: string;              // Required for proxy mode
  timeout?: number;                   // Default: 30000ms
  retries?: number;                   // Default: 3
}

Authentication Modes

Cloudflare Worker Proxy (Recommended)

const config = {
  authMode: 'cloudflare_proxy',
  cloudflareWorkerUrl: 'https://your-proxy.workers.dev',
  appId: 'your-frontend-id',
  workerSecret: 'your-worker-secret',
};

Direct API Access (Legacy)

const config = {
  authMode: 'direct',
  apiUrl: 'https://your-mautic-instance.com',
};

Form Field Types

The package supports all standard Mautic field types:

  • text - Text input
  • email - Email input with validation
  • tel - Phone number input
  • url - URL input
  • textarea - Multi-line text input
  • select - Dropdown selection
  • checkbox - Single checkbox
  • radio - Radio button group

Customization

Custom Form Styling

<MauticForm
  formId="1"
  className="custom-form"
  // Custom CSS classes will be applied to form elements
/>

Custom Validation

import { validateField } from '@marvalt/madapter';

const customValidation = (field, value) => {
  const baseError = validateField(field, value);
  if (baseError) return baseError;
  
  // Add custom validation logic
  if (field.alias === 'custom_field' && value.length < 5) {
    return 'Custom field must be at least 5 characters';
  }
  
  return null;
};

Error Handling & Common Pitfalls

import { MauticForm } from '@marvalt/madapter';

function FormWithErrorHandling() {
  const handleError = (error) => {
    console.error('Form submission error:', error);
    // Custom error handling logic
  };

  return (
    <MauticForm
      formId="1"
      onError={handleError}
      onSuccess={(data) => console.log('Success:', data)}
    />
  );
}

Tips to avoid errors:

  • 404/HTML after submit: Some Mautic setups return an HTML page (even 404) after a successful submit. The client treats this as success. If you implement custom submission, do not rely on server redirects; handle redirects client‑side using the form’s configured postAction.
  • Redirects: Prefer client‑side redirects. The client sets mauticform[return] to an empty string and performs the redirect on the frontend.
  • Dev tracking: Tracking is disabled in VITE_DEV/development builds by design; don’t expect mtc.js to load locally.
  • Proxy headers: In proxy mode the client sends x-app-id and x-worker-secret automatically. Ensure VITE_FRONTEND_ID and VITE_FRONTEND_SECRET are set.
  • Forms data: If you reference a form ID that doesn’t exist in your Mautic instance, you’ll receive a 404 from the API. Use the static data generator or fetch /forms via the Worker to confirm IDs.

Development

Building the Package

npm run build

Running Tests

npm test
npm run test:watch
npm run test:coverage

Linting

npm run lint
npm run lint:fix

License

GPL-3.0-or-later. See LICENSE.

Support

For support and questions, please contact:

Changelog

1.0.0

  • Initial release
  • Mautic API client
  • React components and hooks
  • Static data generation
  • Event tracking
  • TypeScript support