Skip to main content

Creating Custom Panels Layouts & Content Types

Jonathan Adams | Solutions Engineering Director

May 20, 2014

The Panels module provides a flexible and intuitive way to place content throughout your site. This blog post will show you how to use and customize some of the features in panels including layouts and content types.


As it sounds, layouts allow users to choose where each piece of content on a given panel can be rendered. There are two different types of layouts. Builders allow users to create a custom layout inside the GUI via the layout Designer, while predefined layouts allow you to write the actual html to be used within your layout. This blog post will cover the latter option.

When creating a layout you can create one either inside your theme or within your module. For this example we will be creating a two-column layout using the theme implementation. In your theme create a folder named “layouts” and in your file add the following lines.

; Panel Layouts
plugins[panels][layouts] = layouts

This line tells the theme to look for custom layouts within our layouts directory inside our theme. Inside of your Layout’s folder, create a folder called one_third_left and inside of that folder create three files,, one-third-left.tpl.php, one-third-left.css. Your directory structure should be as follows: 


The .inc file controls the regions that are present as well as the theme and css to be used within the layout. Open the file and add the following lines.


 * Implements hook_panels_layouts()
function theme_name_one_third_left_panels_layouts() {

  $items['one_third_left'] = array(
    'title' => t('Theme Name: One Third Left'),
    'category' => t('Theme Name'),
    'icon' => 'one-third-left.png',
    'theme' => 'one-third-left',
    'css' => 'one-third-left.css',
    'regions' => array(
      'header' => t('Header'),
      'content' => t('Content'),
      'sidebar' => t('Sidebar'),

  return $items;

This array defines all the needed items for a layout to appear. The title as it sounds, is the title of the panel we created. Category is used to group similar layouts together. When choosing a layout you first need to select the category and then the corresponding layouts will be shown. The icon is a visible representation to help users see what the layout looks like at a glance (The icon was omitted for this tutorial). The theme is the tpl file used to control the layout and html. Css controls the styling of the layout, it is also important to know that the css file is also used to control the layout shown inside the gui. Regions are used to define where content can be placed inside of the layout.

Open one-third-left.tpl.php and add the following lines.

<div <?php !empty($css_id) ? print 'id="' . $css_id . '"' : ''; ?>>
  <div class="columns">
    <div class="header">
      <?php print $content['header'];?>
  <div class="medium-4 columns left-sidebar">
    <?php print $content['sidebar'];?>
  <div class="medium-8 columns">
    <?php print $content['content'];?>

In this tpl example, we are creating a 2 column layout with a header. Notice the $content variable. Each of these corresponds to a region that we created inside of our .inc file. To add more regions simply add them to the .inc file and place them in the tpl. I will not cover the css in this tutorial but once cache is cleared you should now see your new layout available for use.

Content Types

Ctools content types are like blocks in the sense that they both provide an easy way to create pieces of content that can be placed throughout a site. Where content types excel is in the configuration. While blocks allow you to configure them by means of a form, those settings are global, so every instance of a block will have the same settings no matter where it is placed. Content Types on the other hand allow you to create one content type and have different settings applied. This portion of the tutorial will discuss how a ctools content type can be created.

First add ctools as a dependency in your file.

dependencies[] = ctools

Create a folder named “plugins” inside of your module directory. Next add a folder named “content_types” inside of the newly created plugins directory. Then add the following lines to your module_name.module file.


 * Implements hook_ctools_plugin_directory().
function module_name_ctools_plugin_directory($owner, $plugin_type) {
  if ($owner == 'ctools' && $plugin_type == 'content_types') {
    return 'plugins/content_types';

This hook tells ctools where to look for its content types. In this example we will create a simple content type that displays the 5 newest published nodes and allows the user to choose whether or not the title is linkable.

Inside the content_type folder, create another folder called newest_node and inside that folder create a file called Add the following lines to the file.


$plugin = array(
  'title' => t('Newest Nodes'),
  'description' => t('Display a list of the newest nodes'),
  'single' => TRUE,
  'render callback' => 'newest_nodes_content_type_render',
  'defaults' => array(),
  'edit form' => 'newest_nodes_edit_form',
  'category' => array(t('My Module')),

This array is used to define our settings for the content type. As it sounds, title is the title of the content type. Single is used to show that our content type has no subtypes. The render callback is the function that is used to generate the markup for our content type. Defaults is the default ctools context. In our case we do not have one. The edit form is the form function used to generate the content type settings. Finally category is used to group similar content types. To add the form add the following lines.

 * Content type settings form.
function newest_nodes_edit_form($form, &$form_state) {

  $conf = $form_state['conf'];

  $form['number'] = array(
    '#type' => 'select',
    '#title' => t('Select the number of node to display'),
    '#options' => array(5 => t('Five'), 10 => t('Ten'), 15 => t('Fifteen')),
    '#required' => TRUE,
    '#default_value' => isset($conf['number']) ? $conf['number'] : ''

  $form['link'] = array(
    '#type' => 'checkbox',
    '#title' => t('Link title to node'),
    '#default_value' => isset($conf['link']) ? $conf['link'] : '',

  return $form;

 * Content type submit handler.
function newest_nodes_edit_form_submit($form, &$form_state) {
  foreach (array('number', 'link') as $key) {
    $form_state['conf'][$key] = $form_state['values'][$key];

This simple form let users decide how many nodes to display and whether or not a link should be displayed. Now lets proceed with adding the actual render function.

function newest_nodes_content_type_render($subtype, $conf, $args, $context) {

  $results = db_select('node', 'n')
    ->fields('n', array('nid', 'title'))
    ->condition('status', 1)
    ->range(0, $conf['number'])

  $items = array();

  foreach ($results as $result) {
    if (isset($conf['link'])) {
      $items[] = l($result->title, 'node/' . $result->nid);
    else {
      $items[] = $result->title;

  $block = new stdClass();
  $block->title = t('Newest Nodes');
  $block->content = theme('item_list', array('items' => $items));

  return $block;

This function does a simple db_select using the variables we set in our form to limit the number of items displayed and to also decide whether or not to display a link. Clear your cache and you should now see your newly created content type available along with the settings form. 


Finally here is what my Content Type looks like once styled.


While my previous example is a bit on the simplistic side it does highlight the power of ctools layouts and content types. When used with the appropriate CSS responsive layouts can be created and easily changed per node type. For more information on the Panels Module, check out some of our other Phase2 thoughts.

Recommended Next
A Developer's Guide For Contributing To Drupal
Black pixels on a grey background
3 Steps to a Smooth Salesforce Integration
Black pixels on a grey background
Drupal 8 End of Life: What You Need To Know
Woman working on a laptop
Jump back to top