How to create a newsletter subscription form using Uniform & Mailchimp?

Tried to do what you’ve said but the problem remains… grandchildren pages gets the following error on submit:

 ! Fatal error: Uncaught No Uniform actions were given.
 thrown in /Users/me/Sites/masite/site/plugins/uniform/lib/UniForm.php on line 126

Yes, the messages are defined in the language files:

l::set('uniform-fields-required', 'Please fill in all required fields.');
l::set('uniform-fields-not-valid', 'Some fields do not contain valid data.');

This affects all forms on the whole site, though. If you have multiple forms and only one of them should have different messages you have to do a little hack: You have to change the language strings in the controller/route just before the uniform() call and then reset them again afterwards.

And this error doesn’t occur if you use the (:all)/newsletter/subscribe route? This doesn’t make sense :confused:
The route is working properly since Uniform is called but an error like that is only thrown if the actions array is misconfigured/empty. Did you change anything in the route besides the pattern?

I ended up using jquery to show these different messages… not ideal but it’ll do till I figure out how to do this… because this site isn’t multilang so I’ve modified UniForm.php to use c::get instead of l::get… and defined those text messages in the config file.

No… it works perfectly.
I also don’t know why.

Absolutely nothing… these subpages/grandchildrem… they have another form that uses Uniform… but I’ve checked their vars and no naming conflict.

Here’s the whole form/jquery and the router:

<section class="newsletter">

  <form id="newsletter" method="post">

    <input type="hidden" name="_submit" value="<?php echo uniform('newsletter-subscription')->token() ?>">
    <label class="hidden form__potty" for="website">Please leave this field blank</label>
    <input type="text" name="website" id="website" class="hidden form__potty" />

    <div class="hidden" id="feedback">
      <div class="feedback-message">Sending...</div>

      <label class="hidden" for="email">Email *</label>
      <input  type="email" name="_from" id="email" value="" placeholder="Email" required />

      <button type="submit">Sign up</button>


    $('#newsletter').on('submit', function(e){

        var form = $(this); 
        var feedback = $('#feedback');

            type: 'POST',
            // use the same url here as the 'pattern' in your route
            url: '<?php echo $page->url() ?>/newsletter/subscribe',
            data: form.serialize(),

            // this success means the ajax call reached the api/action
            success: function(response){ 

              if (response.success) { // show messages from mailchimp action 
                var msg = "<b>Thank you</b> <br>"; 
                msg += response.message;

                form.find('#email, button').prop('disabled', 'disabled');

              } else { // a validation error was encountered 
                var msg = "<b>Oops</b> <br>"; 
                msg += "This email looks wrong.";

            // the form was unable to reach processor api
            error: function(response){
              feedback.find('.feedback-message').text('An error has occurred.');
            dataType: 'json'

            'scrollTop':   $('#newsletter').offset().top
        }, 400);

The router:

    // in 'pattern', enter the same url being called from your ajax javascript function
    'pattern' => '(:all)/newsletter/subscribe',
    'method' => 'POST',
    'action' => function() {

      // check whether this is an ajax request, and respond with an error if it isn't
      if (!kirby()->request()->ajax()) return site()->errorPage();

      $newsletter = uniform('newsletter-subscription', [
         'required' => [
            '_from' => 'email'
         'actions' => [[
            '_action' => 'mailchimp',
            'api_key' => 'xxxxxxxxxxxxxxxxxxxxx-us6',
            'list_id' => 'xxxxxxx', 

      // get the names of all erroneous fields
      $errors = array_keys(array_filter(get(), function ($field) use ($newsletter) {
         return $newsletter->hasError($field);

      return response::json([
         'success' => $newsletter->successful(),
         'message' => trim($newsletter->message()),
         'errors' => $errors,


1 Like

So everything works as expected now?

Yes indeed Martin… again, thanks a lot for all your help. :vulcan:

Wow, epic thread!
The resulting code would make for a great “how-to” post :slight_smile:


Thanks @Malvese, I’ll try to do something like @luxlogica did here.

It was hugely helpful.