diff --git a/project_dependency.module b/project_dependency.module
index 8adfa4a..a39203b 100644
--- a/project_dependency.module
+++ b/project_dependency.module
@@ -10,6 +10,190 @@ define('PROJECT_DEPENDENCY_DEPENDENCY_REQUIRED', 0);
 define('PROJECT_DEPENDENCY_DEPENDENCY_RECOMMENDED', 1);
 
 /**
+ * Implements hook_menu().
+ */
+function project_dependency_menu() {
+  $items = array();
+
+  $items['node/%node/details'] = array(
+    'title' => 'Details',
+    'page callback' => 'project_dependencies_details',
+    'page arguments' => array(1),
+    'access callback' => 'project_dependency_access_check',
+    'access arguments' => array(1),
+    'weight' => 1,
+    'type' => MENU_LOCAL_TASK,
+  );
+
+  return $items;
+}
+
+/**
+ * Restrict tab to release nodes.
+ */
+function project_dependency_access_check($node) {
+  if ($node->type == 'project_release') {
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Dependencies page.
+ */
+function project_dependencies_details($node) {
+  $content = '<h3>' . t('Components') . '</h3>';
+  $components = project_dependency_components($node);
+  $content .= drupal_render($components);
+  return $content;
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function project_dependency_block_info() {
+  $blocks['components'] = array(
+    'info' => t('Project components'),
+  );
+  $blocks['dependencies'] = array(
+    'info' => t('Project release dependencies'),
+  );
+  return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function project_dependency_block_view($delta = '') {
+  switch ($delta) {
+    case 'components':
+      $node = menu_get_object();
+      if ($node && $node->nid && $node->type == 'project_release') {
+        // You have a valid Project Release node to work with.
+        $path = explode('/', current_path());
+        if (count($path) == 2) {
+          // This is not the edit tab.
+          $block['subject'] = t('Components');
+          $block['content'] = project_dependency_components($node);
+          return $block;
+        }
+      }
+      break;
+
+    case 'dependencies':
+      $node = menu_get_object();
+      if ($node && $node->nid && $node->type == 'project_release') {
+        // You have a valid Project Release node to work with.
+        $path = explode('/', current_path());
+        if (count($path) == 2 || $path[2] == 'details') {
+          // This is not the edit tab.
+          $block['subject'] = t('Dependencies');
+          $block['content'] = project_dependency_dependencies($node);
+          return $block;
+        }
+      }
+      break;
+  }
+}
+
+/**
+ * Return HTML with the project components.
+ */
+function project_dependency_components($node) {
+  $content = '';
+  $header = array(
+    t('Title'),
+    t('Name'),
+  );
+  $count = db_select('project_dependency_component', 'components')
+    ->condition('components.release_nid', $node->nid)
+    ->fields('components', array('name', 'title'))
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+  if ($count == 0) {
+    // Reprocess the release.
+    module_load_include('inc', 'project_dependency',
+      'project_dependency.drupal');
+    $pid = project_dependency_field_value($node, 'field_release_project',
+      'target_id');
+    $project = node_load($pid);
+    $shortname = project_dependency_field_value($project,
+      'field_project_machine_name');
+    project_dependency_process_release($shortname, $node);
+  }
+  $components = db_select('project_dependency_component', 'components')
+    ->condition('components.release_nid', $node->nid)
+    ->fields('components', array('name', 'title'))
+    ->orderBy('components.title')
+    ->execute();
+  $rows = array();
+  foreach ($components as $component) {
+    $row = array(
+      'data' => array(
+        $component->title,
+        $component->name,
+      ),
+    );
+    $rows[] = $row;
+  }
+  $content['table'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+    '#empty' => t('No entries in the Project Dependency component table for this release.'),
+  );
+  return $content;
+}
+
+/**
+ * Return HTML with the project dependencies.
+ */
+function project_dependency_dependencies($node) {
+  $content = '';
+  $dependencies
+    = project_dependency_get_external_release_dependencies($node->nid);
+  $header = array(
+    t('Project'),
+    t('Selected release'),
+  );
+  $required_rows = array();
+  $optional_rows = array();
+  foreach ($dependencies as $dependency) {
+    $row = array(
+      'data' => array(
+        l($dependency['uri'], 'project/' . $dependency['uri']),
+        $dependency['version'],
+      ),
+    );
+    if ($dependency['dependency_type'] == 0) {
+      $required_rows[] = $row;
+    }
+    else {
+      $optional_rows[] = $row;
+    }
+  }
+  $content .= '<p>' . t('The selected release is the release that will be used for automated testing.  Optional projects are used for testing.') . '</p>';
+  $content .= '<h4>' . t('Required') . '</h4>';
+  $content .= theme('table',
+    array(
+      'header' => $header,
+      'rows' => $required_rows,
+      'empty' => 'No required projects',
+    )
+  );
+  $content .= '<h4>' . t('Optional') . '</h4>';
+  $content .= theme('table',
+    array(
+      'header' => $header,
+      'rows' => $optional_rows,
+      'empty' => 'No optional projects',
+    )
+  );
+  return $content;
+}
+
+/**
  * Implements hook_project_release_create_package().
  *
  * When a release node is created, we build the dependencies.
