diff --git a/flag.libraries.yml b/flag.libraries.yml
index e4322a6..1df884b 100644
--- a/flag.libraries.yml
+++ b/flag.libraries.yml
@@ -8,8 +8,19 @@ flag.admin:
     - core/drupalSettings
     - core/drupal.form
 
-flag.link:
+# Normal action link library.
+flag.action_link:
   version: VERSION
   css:
     theme:
       css/flag-link.css: {}
+
+# Ajax action link library.
+flag.ajax_action_link:
+  version: VERSION
+  js:
+    js/flag-action_link_flash.js: {}
+  dependencies:
+    - core/jquery
+    - core/drupal
+    - core/drupal.ajax
\ No newline at end of file
diff --git a/flag.module b/flag.module
index 934073c..d61bdf3 100644
--- a/flag.module
+++ b/flag.module
@@ -270,16 +270,20 @@ function flag_entity_extra_field_info() {
  * Implements hook_theme().
  */
 function flag_theme() {
+  // Base theme definition used by all action links.
+  $action_base = [
+    'variables' => [
+      'attributes' => [],
+      'title' => NULL,
+      'action' => 'flag',
+      'flag' => NULL,
+      'flaggable' => NULL,
+    ]
+  ];
+
   return [
-    'flag' => [
-      'variables' => [
-        'attributes' => [],
-        'title' => NULL,
-        'action' => 'flag',
-        'flag' => NULL,
-        'flaggable' => NULL,
-      ],
-    ],
+    'flag__action_link' => $action_base,
+    'flag__ajax_action_link' => $action_base,
   ];
 }
 
diff --git a/flag.routing.yml b/flag.routing.yml
index 4538d68..faf0159 100644
--- a/flag.routing.yml
+++ b/flag.routing.yml
@@ -47,6 +47,23 @@ flag.action_link_flag:
     _format: 'html'
     _csrf_token: 'TRUE'
 
+flag.ajax_action_link_flag:
+  path: '/flag/ajax/flag/{flag}/{entity_id}'
+  defaults:
+    _controller: '\Drupal\flag\Controller\AjaxActionLinkController::flag'
+  requirements:
+    _flag_access: 'entity:flag{flag}'
+    _csrf_token: 'TRUE'
+
+flag.ajax_fallback_action_link_flag:
+  path: '/flag/nojs/flag/{flag}/{entity_id}'
+  defaults:
+    _controller: '\Drupal\flag\Controller\ActionLinkController::flag'
+  requirements:
+    _flag_access: 'entity:flag{flag}'
+    _format: 'html'
+    _csrf_token: 'TRUE'
+
 flag.action_link_unflag:
   path: '/flag/unflag/{flag}/{entity_id}'
   defaults:
@@ -56,6 +73,23 @@ flag.action_link_unflag:
     _format: 'html'
     _csrf_token: 'TRUE'
 
+flag.ajax_action_link_unflag:
+  path: '/flag/ajax/unflag/{flag}/{entity_id}'
+  defaults:
+    _controller: '\Drupal\flag\Controller\AjaxActionLinkController::unflag'
+  requirements:
+    _unflag_access: 'entity:flag{flag}'
+    _csrf_token: 'TRUE'
+
+flag.ajax_fallback_action_link_unflag:
+  path: '/flag/nojs/unflag/{flag}/{entity_id}'
+  defaults:
+    _controller: '\Drupal\flag\Controller\ActionLinkController::unflag'
+  requirements:
+    _unflag_access: 'entity:flag{flag}'
+    _format: 'html'
+    _csrf_token: 'TRUE'
+
 flag.confirm_flag:
   path: '/flag/confirm/flag/{flag}/{entity_id}'
   defaults:
diff --git a/js/flag-action_link_flash.js b/js/flag-action_link_flash.js
new file mode 100644
index 0000000..03317d3
--- /dev/null
+++ b/js/flag-action_link_flash.js
@@ -0,0 +1,31 @@
+/**
+ * @file
+ * Defines Javascript behaviors for the flag module's AJAX links.
+ */
+
+(function ($, Drupal) {
+
+  'use strict';
+
+  /**
+   * Display message on the screen for a short period of time.
+   *
+   * @param {Drupal.Ajax} [ajax]
+   *   The ajax object.
+   * @param {object} response
+   *   Object holding the server response.
+   * @param {string} response.selector
+   *   The selector of link to be updated.
+   * @param {string} response.message
+   *   The message to be displayed.
+   * @param {number} [status]
+   *   The HTTP status code.
+   */
+  Drupal.AjaxCommands.prototype.actionLinkFlash = function(ajax, response, status){
+    // Place content in current-msg div.
+    $(response.selector).after('<span>' + response.message + '</span>')
+      .fadeIn('fast')
+      .fadout('slow');
+  }
+
+})(jQuery, Drupal);
diff --git a/src/Ajax/ReplaceActionLinkCommand.php b/src/Ajax/ReplaceActionLinkCommand.php
new file mode 100644
index 0000000..54f94ca
--- /dev/null
+++ b/src/Ajax/ReplaceActionLinkCommand.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Drupal\flag\Ajax;
+
+use Drupal\Core\Ajax\CommandInterface;
+
+/**
+ * An AJAX command class to update stale action links.
+ *
+ * A message is pulsed on the screen after update.
+ *
+ * @ingroup flag
+ */
+class ActionLinkFlashCommand implements CommandInterface {
+
+  /**
+   * The message to be "pulsed" under the link.
+   *
+   * @var string
+   */
+  protected $message;
+
+  /**
+   * Construct a command to update the action link.
+   *
+   * @param string $selector
+   *   Identifies the action link to be replaced.
+   * @param string $message
+   *   (optional) The message to be displayed. Defaults to ''.
+   */
+  public function __construct($selector, $message = '') {
+    $this->selector = $selector;
+    $this->message = $message;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function render() {
+    return [
+      'command' => 'actionLinkFlash',
+      'selector' => $this->selector,
+      'message' => $this->message,
+    ];
+  }
+
+}
diff --git a/src/Ajax/UpdateActionLinkResponse.php b/src/Ajax/UpdateActionLinkResponse.php
new file mode 100644
index 0000000..ecaf1fa
--- /dev/null
+++ b/src/Ajax/UpdateActionLinkResponse.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Drupal\flag\Ajax;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\flag\FlagInterface;
+
+/**
+ * A commands sequence to update a given action link.
+ *
+ * A series of AJAX commands bound together into a JSON response.
+ *
+ * @ingroup flag
+ */
+class UpdateActionLinkResponse extends AjaxResponse {
+
+  /**
+   * Construct a AJAX response that updates the action link.
+   *
+   * @param \Drupal\flag\FlagInterface $flag
+   *   The Flag in the action link.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The Entity in the action link.
+   * @param string $message
+   *   (optional) The message to be displayed. Defaults to ''.
+   */
+  public function __construct(FlagInterface $flag, EntityInterface $entity, $message = '') {
+    // Initialise bare AJAX response.
+    parent::__construct();
+
+    // Identify the action link.
+    $selector = 'flag-link-' . $flag->id() . '-' . $entity->id();
+
+    // Get a replacement action link (as a string).
+    /* @todo Is there a simpler way of rendering the link? */
+    $link = $flag->getLinkTypePlugin()->getLink($flag, $entity);
+    /** @var \Drupal\Core\Render\Renderer $renderer */
+    $renderer = \Drupal::service('renderer');
+    $content = $renderer->renderPlain($link);
+
+    // Push a replace command onto the stack.
+    $replace = new ReplaceCommand($selector, $content);
+    $this->addCommand($replace);
+
+    // Push a message pulsing comamnd onto the stack.
+    $pulse = new ActionLinkFlashCommand($selector, $message);
+    $this->addCommand($pulse);
+  }
+
+}
diff --git a/src/Controller/ActionLinkController.php b/src/Controller/ActionLinkController.php
index 7279e7d..8734f93 100644
--- a/src/Controller/ActionLinkController.php
+++ b/src/Controller/ActionLinkController.php
@@ -2,23 +2,16 @@
 
 namespace Drupal\flag\Controller;
 
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\ReplaceCommand;
-use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
-use Drupal\Core\Entity\EntityInterface;
-use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
-use Drupal\Core\Render\RendererInterface;
-use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\flag\FlagInterface;
 use Drupal\flag\FlagServiceInterface;
-use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
- * Provides a controller to flag and unflag when routed from a normal link.
+ * Controller responses to flag and unflag requests from a normal link.
  */
-class ActionLinkController extends ControllerBase implements ContainerInjectionInterface {
-
+class ActionLinkController implements ContainerInjectionInterface {
   /**
    * The flag service.
    *
@@ -27,21 +20,13 @@ class ActionLinkController extends ControllerBase implements ContainerInjectionI
   protected $flagService;
 
   /**
-   * The renderer service.
-   *
-   * @var \Drupal\Core\Render\RendererInterface
-   */
-  protected $renderer;
-
-  /**
    * Constructor.
    *
    * @param FlagServiceInterface $flag
    *   The flag service.
    */
-  public function __construct(FlagServiceInterface $flag, RendererInterface $renderer) {
+  public function __construct(FlagServiceInterface $flag) {
     $this->flagService = $flag;
-    $this->renderer = $renderer;
   }
 
   /**
@@ -49,27 +34,24 @@ class ActionLinkController extends ControllerBase implements ContainerInjectionI
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('flag'),
-      $container->get('renderer')
+      $container->get('flag')
     );
   }
 
   /**
-   * Performs a flagging when called via a route.
+   * Performs a flagging for a normal link.
    *
    * @param \Drupal\flag\FlagInterface $flag
    *   The flag entity.
    * @param int $entity_id
    *   The flaggable entity ID.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
    *
    * @return \Symfony\Component\HttpFoundation\RedirectResponse
    *   The response object.
    *
    * @see \Drupal\flag\Plugin\Reload
    */
-  public function flag(FlagInterface $flag, $entity_id, Request $request) {
+  public function flag(FlagInterface $flag, $entity_id) {
     /* @var \Drupal\Core\Entity\EntityInterface $entity */
     $entity = $this->flagService->getFlaggableById($flag, $entity_id);
 
@@ -77,29 +59,30 @@ class ActionLinkController extends ControllerBase implements ContainerInjectionI
       $this->flagService->flag($flag, $entity);
     }
     catch (\LogicException $e) {
-      // Fail silently so we return to the entity, which will show an updated
-      // link for the existing state of the flag.
+      // Back to the entity, without success.
+      return new RedirectResponse($entity->toUrl()->toUriString());
     }
 
-    return $this->generateResponse($flag, $entity, $request);
+    drupal_set_message($flag->getFlagMessage());
+
+    // Back to the entity.
+    return new RedirectResponse($entity->toUrl()->toUriString());
   }
 
   /**
-   * Performs a flagging when called via a route.
+   * Performs a unflagging for a normal link.
    *
    * @param \Drupal\flag\FlagInterface $flag
    *   The flag entity.
    * @param int $entity_id
    *   The flaggable entity ID.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
    *
-   * @return \Symfony\Component\HttpFoundation\RedirectResponse
+   * @return \Symfony\Component\HttpFoundation\RedirectResponse|null
    *   The response object.
    *
    * @see \Drupal\flag\Plugin\Reload
    */
-  public function unflag(FlagInterface $flag, $entity_id, Request $request) {
+  public function unflag(FlagInterface $flag, $entity_id) {
     /* @var \Drupal\Core\Entity\EntityInterface $entity */
     $entity = $this->flagService->getFlaggableById($flag, $entity_id);
 
@@ -107,55 +90,14 @@ class ActionLinkController extends ControllerBase implements ContainerInjectionI
       $this->flagService->unflag($flag, $entity);
     }
     catch (\LogicException $e) {
-      // Fail silently so we return to the entity, which will show an updated
-      // link for the existing state of the flag.
+      // Back to the entity, without success.
+      return new RedirectResponse($entity->toUrl()->toUriString());
     }
 
-    return $this->generateResponse($flag, $entity, $request);
-  }
-
-  /**
-   * Generates a response object after handing the un/flag request.
-   *
-   * Depending on the wrapper format of the request, it will either redirect
-   * or return an ajax response.
-   *
-   * @param \Drupal\flag\FlagInterface $flag
-   *   The flag entity.
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity object.
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request.
-   *
-   * @return \Drupal\Core\Ajax\AjaxResponse|\Symfony\Component\HttpFoundation\RedirectResponse
-   *   The response object.
-   */
-  protected function generateResponse(FlagInterface $flag, EntityInterface $entity, Request $request) {
-    if ($request->get(MainContentViewSubscriber::WRAPPER_FORMAT) == 'drupal_ajax') {
-      // Create a new AJAX response.
-      $response = new AjaxResponse();
-
-      // Get the link type plugin.
-      $link_type = $flag->getLinkTypePlugin();
-
-      // Generate the link render array.
-      $link = $link_type->getLink($flag, $entity);
-
-      // Generate a CSS selector to use in a JQuery Replace command.
-      $selector = '.flag-' . $flag->id() . '-' . $entity->id();
-
-      // Create a new JQuery Replace command to update the link display.
-      $replace = new ReplaceCommand($selector, $this->renderer->renderPlain($link));
-      $response->addCommand($replace);
-
-      return $response;
-    }
-    else {
-      // Redirect back to the entity. A passed in destination query parameter
-      // will automatically override this.
-      $url_info = $entity->toUrl();
-      return $this->redirect($url_info->getRouteName(), $url_info->getRouteParameters());
-    }
+    drupal_set_message($flag->getUnflagMessage());
 
+    // Back to the entity.
+    return new RedirectResponse($entity->toUrl()->toUriString());
   }
+
 }
diff --git a/src/Controller/AjaxActionLinkController.php b/src/Controller/AjaxActionLinkController.php
new file mode 100644
index 0000000..2224bf3
--- /dev/null
+++ b/src/Controller/AjaxActionLinkController.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Drupal\flag\Controller;
+
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\flag\Ajax\UpdateActionLinkResponse;
+use Drupal\flag\FlagInterface;
+use Drupal\flag\FlagServiceInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Controller responses to flag and unflag responses from a AJAX link.
+ */
+class AjaxActionLinkController implements ContainerInjectionInterface {
+  /**
+   * The flag service.
+   *
+   * @var \Drupal\flag\FlagServiceInterface
+   */
+  protected $flagService;
+
+  /**
+   * Constructor.
+   *
+   * @param FlagServiceInterface $flag
+   *   The flag service.
+   */
+  public function __construct(FlagServiceInterface $flag) {
+    $this->flagService = $flag;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('flag')
+    );
+  }
+
+  /**
+   * Performs an AJAX flagging.
+   *
+   * @param \Drupal\flag\FlagInterface $flag
+   *   The flag entity.
+   * @param int $entity_id
+   *   The flaggable entity ID.
+   *
+   * @return \Drupal\flag\Ajax\UpdateActionLinkResponse|null
+   *   The response object, only if successful.
+   *
+   * @see \Drupal\flag\Plugin\Reload
+   */
+  public function flag(FlagInterface $flag, $entity_id) {
+    /* @var \Drupal\Core\Entity\EntityInterface $entity */
+    $entity = $this->flagService->getFlaggableById($flag, $entity_id);
+
+    try {
+      $this->flagService->flag($flag, $entity);
+    }
+    catch (\LogicException $e) {
+      // Fail silently, issuing no response forces an AJAX timeout.
+    }
+
+    // Respond by updating the link.
+    return new UpdateActionLinkResponse($flag, $entity, $flag->getFlagMessage());
+  }
+
+  /**
+   * Performs an AJAX unflagging.
+   *
+   * @param \Drupal\flag\FlagInterface $flag
+   *   The flag entity.
+   * @param int $entity_id
+   *   The flaggable entity ID.
+   *
+   * @return \Drupal\flag\Ajax\UpdateActionLinkResponse|null
+   *   The response object, only if successful.
+   *
+   * @see \Drupal\flag\Plugin\Reload
+   */
+  public function unflag(FlagInterface $flag, $entity_id) {
+    /* @var \Drupal\Core\Entity\EntityInterface $entity */
+    $entity = $this->flagService->getFlaggableById($flag, $entity_id);
+
+    try {
+      $this->flagService->flag($flag, $entity);
+    }
+    catch (\LogicException $e) {
+      // Fail silently, issuing no response forces an AJAX timeout.
+    }
+
+    // Respond by updating the link.
+    return new UpdateActionLinkResponse($flag, $entity, $flag->getUnflagMessage());
+  }
+
+}
diff --git a/src/Plugin/ActionLink/AJAXactionLink.php b/src/Plugin/ActionLink/AJAXactionLink.php
index f58f7ec..c750b35 100644
--- a/src/Plugin/ActionLink/AJAXactionLink.php
+++ b/src/Plugin/ActionLink/AJAXactionLink.php
@@ -2,7 +2,6 @@
 
 namespace Drupal\flag\Plugin\ActionLink;
 
-use Drupal\flag\ActionLink\ActionLinkTypeBase;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\flag\FlagInterface;
 
@@ -25,9 +24,26 @@ class AJAXactionLink extends Reload {
    */
   public function buildLink($action, FlagInterface $flag, EntityInterface $entity) {
     $render = parent::buildLink($action, $flag, $entity);
-    $render['#attached']['library'][] = 'core/drupal.ajax';
-    $render['#attributes']['class'][] = 'use-ajax';
+    // Divert to a theme that adds AJAX functionality.
+    $render['#theme'] = 'flag-ajax';
     return $render;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function routeName($action = NULL) {
+    // Build links using the appropiate fallback route, if the user agent has
+    // javascript enabled the route will be upgraded to a route of the form
+    // flag.ajax_action_link_(un)flag.
+    if ($action === 'unflag') {
+      $return = 'flag.ajax_fallback_action_link_unflag';
+    }
+    else {
+      $return = 'flag.ajax_fallback_action_link_flag';
+    }
+
+    return $return;
+  }
+
 }
diff --git a/templates/flag.html.twig b/templates/flag.html.twig
deleted file mode 100644
index b7080a1..0000000
--- a/templates/flag.html.twig
+++ /dev/null
@@ -1,35 +0,0 @@
-{#
-/**
- * @file
- * Default theme implementation for flag links.
- *
- * Available variables:
- * - attributes: HTML attributes for the link element.
- * - title: The flag link title.
- * - action: 'flag' or 'unflag'
- * - flag: The flag object.
- * - flaggable: The flaggable entity.
- */
-#}
-{% spaceless %}
-{# Attach the flag CSS library.#}
-{{ attach_library('flag/flag.link') }}
-
-{# Depending on the flag action, set the appropriate action class. #}
-{% if action == 'unflag' %}
-    {% set action_class = 'action-unflag' %}
-{% else %}
-    {% set action_class = 'action-flag' %}
-{% endif %}
-
-{# Set the remaining Flag CSS classes. #}
-{% set classes = [
-'flag',
-'flag-' ~ flag.id(),
-'flag-' ~ flag.id() ~ '-' ~ flaggable.id(),
-action_class
-] %}
-{% set attributes = attributes.addClass(classes) %}
-
-<a{{ attributes }}>{{ title }}</a>
-{% endspaceless %}
diff --git a/templates/flag__action_link.html.twig b/templates/flag__action_link.html.twig
new file mode 100644
index 0000000..5e0968d
--- /dev/null
+++ b/templates/flag__action_link.html.twig
@@ -0,0 +1,37 @@
+{#
+/**
+ * @file
+ * Default theme implementation for action links.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the link element.
+ * - title: The flag link title.
+ * - action: 'flag' or 'unflag'
+ * - flag: The flag object.
+ * - flaggable: The flaggable entity.
+ */
+#}
+{% spaceless %}
+
+  {# Add the CSS library for a normal link. #}
+  {{ attach_library('flag/flag.action_link') }}
+
+  {# Depending on the flag action, set the appropriate action class. #}
+  {% if action == 'unflag' %}
+    {% set action_class = 'action-unflag' %}
+  {% else %}
+    {% set action_class = 'action-flag' %}
+  {% endif %}
+
+  {# Set the remaining Flag CSS classes. #}
+  {% set classes = [
+    'flag',
+    'flag-' ~ flag.id(),
+    'flag-' ~ flag.id() ~ '-' ~ flaggable.id(),
+    action_class
+   ]
+  %}
+
+  <a{{ attributes.addClass(classes) }}>{{ title }}</a>
+
+{% endspaceless %}
diff --git a/templates/flag__ajax_action_link.twig b/templates/flag__ajax_action_link.twig
new file mode 100644
index 0000000..b5cbda3
--- /dev/null
+++ b/templates/flag__ajax_action_link.twig
@@ -0,0 +1,25 @@
+{#
+/**
+ * @file
+ * Augment the normal action link with AJAX functionaliy.
+ *
+ * Available variables:
+ * - attributes: HTML attributes for the link element.
+ * - title: The flag link title.
+ * - action: 'flag' or 'unflag'
+ * - flag: The flag object.
+ * - flaggable: The flaggable entity.
+ */
+#}
+{% spaceless %}
+
+  {# Add the client side 'javascript command' code to style the link. #}
+  {{ attach_library('flag/flag.ajax_action_link') }}
+
+  {# Make the link an AJAX link. #}
+  {% set attributes = attributes.addClass('use-ajax') %}
+
+  {# Extend the default link. #}
+  {% include 'flag.html.twig' %}
+
+{% endspaceless %}
