diff --git modules/contextual/contextual.api.php modules/contextual/contextual.api.php
new file mode 100644
index 0000000..e445bbc
--- /dev/null
+++ modules/contextual/contextual.api.php
@@ -0,0 +1,41 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Hooks provided by Contextual module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Alter a contextual links element before it is rendered.
+ *
+ * This hook is invoked by contextual_pre_render_links(). The renderable array
+ * of #type 'contextual_links', containing the entire contextual links data that
+ * is passed in by reference. Further links may be added or existing links can
+ * be altered.
+ *
+ * @param $element
+ *   A renderable array representing the contextual links.
+ * @param $items
+ *   An associative array containing the original contextual link items, as
+ *   generated by menu_contextual_links(), which were used to build
+ *   $element['#links'].
+ *
+ * @see hook_menu_contextual_links_alter()
+ * @see contextual_pre_render_links()
+ * @see contextual_element_info()
+ */
+function hook_contextual_links_view_alter(&$element, $items) {
+  // Add another class to all contextual link lists to facilitate custom
+  // styling.
+  $element['#attributes']['class'][] = 'custom-class';
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git modules/contextual/contextual.module modules/contextual/contextual.module
index 07829ac..c8812b3 100644
--- modules/contextual/contextual.module
+++ modules/contextual/contextual.module
@@ -6,7 +6,6 @@
  * Adds contextual links to perform actions related to elements on a page.
  */
 
-
 /**
  * Implements hook_help().
  */
@@ -57,9 +56,31 @@ function contextual_library() {
 }
 
 /**
+ * Implements hook_element_info().
+ */
+function contextual_element_info() {
+  $types['contextual_links'] = array(
+    '#pre_render' => array('contextual_pre_render_links'),
+    '#theme' => 'links__contextual',
+    '#links' => array(),
+    '#prefix' => '<div class="contextual-links-wrapper">',
+    '#suffix' => '</div>',
+    '#attributes' => array(
+      'class' => array('contextual-links'),
+    ),
+    '#attached' => array(
+      'library' => array(
+        array('contextual', 'contextual-links'),
+      ),
+    ),
+  );
+  return $types;
+}
+
+/**
  * Template variable preprocessor for contextual links.
  *
- * @see contextual_links_view()
+ * @see contextual_pre_render_links()
  */
 function contextual_preprocess(&$variables, $hook) {
   static $hooks;
@@ -86,10 +107,14 @@ function contextual_preprocess(&$variables, $hook) {
   }
 
   if (isset($element) && is_array($element) && !empty($element['#contextual_links'])) {
-    $variables['title_suffix']['contextual_links'] = contextual_links_view($element);
-    if (!empty($variables['title_suffix']['contextual_links'])) {
-      $variables['classes_array'][] = 'contextual-links-region';
-    }
+    // Initialize the template variable as a renderable array.
+    $variables['title_suffix']['contextual_links'] = array(
+      '#type' => 'contextual_links',
+      '#contextual_links' => $element['#contextual_links'],
+      '#element' => $element,
+    );
+    // Mark this element as potentially having contextual links attached to it.
+    $variables['classes_array'][] = 'contextual-links-region';
   }
 }
 
@@ -113,8 +138,8 @@ function contextual_preprocess(&$variables, $hook) {
  *
  * @see menu_contextual_links()
  */
-function contextual_links_view($element) {
-  static $destination;
+function contextual_pre_render_links($element) {
+  $destination = &drupal_static(__FUNCTION__);
 
   // Retrieve contextual menu links.
   $items = array();
@@ -123,37 +148,31 @@ function contextual_links_view($element) {
   }
 
   // Transform contextual links into parameters suitable for theme_link().
+  $links = array();
   if (!isset($destination)) {
     $destination = drupal_get_destination();
   }
-
-  $links = array();
   foreach ($items as $class => $item) {
     $class = drupal_html_class($class);
     $links[$class] = array(
       'title' => $item['title'],
       'href' => $item['href'],
     );
-    // @todo theme_links() should *really* use the same parameters as l()...
-    if (!isset($item['localized_options']['query'])) {
-      $item['localized_options']['query'] = array();
-    }
+    // @todo theme_links() should *really* use the same parameters as l().
+    $item['localized_options'] += array('query' => array());
     $item['localized_options']['query'] += $destination;
     $links[$class] += $item['localized_options'];
   }
-  $build = array();
-  if ($links) {
-    $build = array(
-      '#prefix' => '<div class="contextual-links-wrapper">',
-      '#suffix' => '</div>',
-      '#theme' => 'links__contextual',
-      '#links' => $links,
-      '#attributes' => array('class' => array('contextual-links')),
-      '#attached' => array(
-        'library' => array(array('contextual', 'contextual-links')),
-      ),
-    );
+  $element['#links'] = $links;
+
+  // Allow modules to alter the renderable contextual links element.
+  drupal_alter('contextual_links_view', $element, $items);
+
+  // If there are no links, tell drupal_render() to abort rendering.
+  if (empty($element['#links'])) {
+    $element['#printed'] = TRUE;
   }
-  return $build;
+
+  return $element;
 }
 
diff --git modules/system/system.api.php modules/system/system.api.php
index 67b5979..660c72a 100644
--- modules/system/system.api.php
+++ modules/system/system.api.php
@@ -1213,9 +1213,10 @@ function hook_menu_local_tasks_alter(&$data, $router_item, $root_path) {
  *   This is a normalized path, which means that an originally passed path of
  *   'node/123' became 'node/%'.
  *
+ * @see hook_contextual_links_view_alter()
  * @see menu_contextual_links()
  * @see hook_menu()
- * @see system_preprocess()
+ * @see contextual_preprocess()
  */
 function hook_menu_contextual_links_alter(&$links, $router_item, $root_path) {
   // Add a link to all contextual links for nodes.
