diff --git a/tabledrag_example/tabledrag_example.info b/tabledrag_example/tabledrag_example.info
new file mode 100644
index 0000000..eb069ab
--- /dev/null
+++ b/tabledrag_example/tabledrag_example.info
@@ -0,0 +1,4 @@
+name = Tabledrag Example
+description = Demonstrates how to create tabledrag forms.
+package = Example modules
+core = 7.x
diff --git a/tabledrag_example/tabledrag_example.install b/tabledrag_example/tabledrag_example.install
new file mode 100644
index 0000000..c53bd1e
--- /dev/null
+++ b/tabledrag_example/tabledrag_example.install
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @file
+ * Install and uninstall functions for the tabledrag example module.
+ *
+ * This file contains the functions required to perform install and
+ * uninstall operations.
+ *
+ */
+
+/**
+ * Implements hook_schema().
+ *
+ * This defines the database table which will hold the example item info.
+ */
+function tabledrag_example_schema() {
+  $schema['tabledrag_example'] = array(
+    'description' => 'Stores some entries for our tabledrag fun.',
+    'fields' => array(
+      'id' => array(
+        'description' => 'The primary identifier for each item',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'name' => array(
+        'description' => 'A name for this item',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'description' => array(
+        'description' => 'A description for this item',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'itemgroup' => array(
+        'description' => 'The group this item belongs to',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'weight' => array(
+        'description' => 'The sortable weight for this item',
+        'type' => 'int',
+        'length' => 11,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'pid' => array(
+        'description' => 'The primary id of the parent for this item',
+        'type' => 'int',
+        'length' => 11,
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'depth' => array(
+        'description' => 'The depth of this item within the tree',
+        'type' => 'int',
+        'size' => 'small',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('id'),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_install().
+ *
+ * This datafills the example item info which will be used in the example.
+ */
+function tabledrag_example_install() {
+  // Insert some values into the database
+  $rows = array(
+    array('name' => st('Item One'), 'description' => st('The first item'), 'itemgroup' => st('Group1')),
+    array('name' => st('Item Two'), 'description' => st('The second item'), 'itemgroup' => st('Group1')),
+    array('name' => st('Item Three'), 'description' => st('The third item'), 'itemgroup' => st('Group1')),
+    array('name' => st('Item Four'), 'description' => st('The fourth item'), 'itemgroup' => st('Group2')),
+    array('name' => st('Item Five'), 'description' => st('The fifth item'), 'itemgroup' => st('Group2')),
+    array('name' => st('Item Six'), 'description' => st('The sixth item'), 'itemgroup' => st('Group2')),
+    array('name' => st('Item Seven'), 'description' => st('The seventh item'), 'itemgroup' => st('Group3')),
+    array('name' => st('A Root Node'), 'description' => st('This item cannot be nested under a parent item'), 'itemgroup' => st('Group3')),
+    array('name' => st('A Leaf Item'), 'description' => st('This item cannot have child items'), 'itemgroup' => st('Group3')),
+  );
+  if (db_table_exists('tabledrag_example')) {
+    foreach ($rows as $row) {
+      db_insert('tabledrag_example')->fields($row)->execute();
+    }
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ *
+ * This removes the example data when the module is uninstalled.
+ */
+function tabledrag_example_uninstall() {
+  db_drop_table('tabledrag_example');
+}
diff --git a/tabledrag_example/tabledrag_example.module b/tabledrag_example/tabledrag_example.module
new file mode 100644
index 0000000..eed3c8b
--- /dev/null
+++ b/tabledrag_example/tabledrag_example.module
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @file
+ * This is an example describing how to build a sortable form using tabledrag
+ *
+ * See:
+ * @link http://drupal.org/node/508796 Extenders @endlink
+ */
+
+/**
+ * Implements hook_help().
+ *
+ * Show a bit of information about this module on the example page
+ */
+function tabledrag_example_help($path, $arg) {
+  switch ($path) {
+    case 'examples/tabledrag_example':
+      return '<p>' . t('The form here is a themed as a table that is sortable using tabledrag handles.') . '</p>';
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function tabledrag_example_menu() {
+  // Basic example with single-depth sorting
+  $items['examples/tabledrag_example_simple'] = array(
+    'title' => 'TableDrag example',
+    'description' => 'Show a page with a sortable tabledrag form',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tabledrag_example_simple_form'),
+    'access callback' => TRUE,
+    // Do not name an includes file the same as your form name, as you may run
+    // into 'undefined index in drupal_retrieve_form()' notices.
+    'file' => 'tabledrag_example_simple_form.inc',
+  );
+  // Basic parent/child example
+  $items['examples/tabledrag_example_parent'] = array(
+    'title' => 'TableDrag example',
+    'description' => 'Show a page with a sortable parent/child tabledrag form',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tabledrag_example_parent_form'),
+    'access callback' => TRUE,
+    'file' => 'tabledrag_example_parent_form.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_theme().
+ *
+ * We need run our forms through custom theme functions in order to build the
+ * table structure which is required by tabledrag.js.  Before we can use our
+ * custom theme functions, we need to implement hook_theme in order to register
+ * them with Drupal.
+ *
+ * We are defining our theme hooks with the same name as the form generation
+ * function so that Drupal automatically calls our theming function when the
+ * form is displayed.
+ */
+function tabledrag_example_theme() {
+  return array(
+    // Theme function for the 'sortable' example
+    'tabledrag_example_simple_form' => array(
+      'render element' => 'form',
+      'file' => 'tabledrag_example_simple_form.inc',
+    ),
+    // Theme function for the 'parent/child' example
+    'tabledrag_example_parent_form' => array(
+      'render element' => 'form',
+      'file' => 'tabledrag_example_parent_form.inc',
+    ),
+  );
+}
diff --git a/tabledrag_example/tabledrag_example_parent_form.inc b/tabledrag_example/tabledrag_example_parent_form.inc
new file mode 100644
index 0000000..5dc6285
--- /dev/null
+++ b/tabledrag_example/tabledrag_example_parent_form.inc
@@ -0,0 +1,318 @@
+<?php
+/**
+ * @file
+ * Example demonstrating a parent/child tabledrag form
+ */
+
+/**
+ * Build the parent-child example form
+ *
+ * @return
+ *   A form array set for theming by theme_tabledrag_example_parent_form()
+ */
+function tabledrag_example_parent_form($form_state) {
+  // Identify that the elements in 'example_items' are a collection, to
+  // prevent Form API from flattening the array when submitted.
+  $form['example_items']['#tree'] = TRUE;
+
+  // Tabledrag will take care of updating the parent_id relationship on each
+  // row of our table when we drag items around, but we need to build out the
+  // initial tree structure ourselves.  This means ordering our items such
+  // that children items come directly after their parent items, and all items
+  // are sorted by weight relative to their siblings.
+  // To keep this from cluttering the actual tabledrag code, we have moved
+  // this to a dedicated function.
+
+  // Fetch the example data from the database, ordered by parent/child/weight.
+  $result = tabledrag_example_parent_get_data();
+
+  // Iterate through each database result
+  foreach ($result as $item) {
+
+    // Create a form entry for this item.
+    //
+    // Each entry will be an array using the the unique id for that item as
+    // the array key, and an array of table row data as the value.
+    $form['example_items'][$item->id] = array(
+
+      // We'll use a form element of type '#markup' to display the item name.
+      'name' => array(
+        '#markup' => $item->name,
+      ),
+
+      // We'll use a form element of type '#textfield' to display the item
+      // description, to demonstrate that form elements can be included in the
+      // table. We limit the input to 255 characters, which is the limit we
+      // set on the database field.
+      'description' => array(
+        '#type' => 'textfield',
+        '#default_value' => $item->description,
+        '#size' => 20,
+        '#maxlength' => 255,
+      ),
+
+      // For parent/child relationships, we also need to add form items to
+      // store the current item's unique id and parent item's unique id.
+      //
+      // We would normally use a hidden element for this, but for this example
+      // we'll use a disabled textfield element called 'id' so that we can
+      // display the current item's id in the table.
+      //
+      // Because tabledrag modifies the #value of this element, we use
+      // '#default_value' instead of '#value' when defining a hidden element.
+      // Also, because tabledrag modifies '#value', we cannot use a markup
+      // element, which does not support the '#value' property. (Markup
+      // elements use the '#markup' property instead.)
+      'id' => array(
+        //'#type' => 'hidden',
+        //'#default_value' => $item->id,
+        '#type' => 'textfield',
+        '#size' => 3,
+        '#default_value' => $item->id,
+        '#disabled' => TRUE,
+      ),
+
+      // The same information holds true for the parent id field as for the
+      // item id field, described above.
+      'pid' => array(
+        //'#type' => 'hidden',
+        //'#default_value' => $item->pid,
+        '#type' => 'textfield',
+        '#size' => 3,
+        '#default_value' => $item->pid,
+      ),
+
+      // The 'weight' field will be manipulated as we move the items around in
+      // the table using the tabledrag activity.  We use the 'weight' element
+      // defined in Drupal's Form API.
+      'weight' => array(
+        '#type' => 'weight',
+        '#title' => t('Weight'),
+        '#default_value' => $item->weight,
+        '#delta' => 10,
+        '#title-display' => 'invisible',
+      ),
+
+      // We'll use a hidden form element to pass the current 'depth' of each
+      // item within our parent/child tree structure to the theme function.
+      // This will be used to calculate the initial amount of indentation to
+      // add before displaying any child item rows.
+      'depth' => array(
+        '#type' => 'hidden',
+        '#value' => $item->depth,
+      ),
+    );
+  }
+
+  // Now we add our submit button, for submitting the form results.
+  //
+  // The 'actions' wrapper used here isn't strictly necessary for tabledrag,
+  // but is included as a Form API recommended practice.
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save Changes'));
+  return $form;
+}
+
+/**
+ * Theme callback for the tabledrag_example_parent_form form
+ *
+ * The theme callback will format the $form data structure into a table and
+ * add our tabledrag functionality.  (Note that drupal_add_tabledrag should be
+ * called from the theme layer, and not from a form declaration.  This helps
+ * keep template files clean and readable, and prevents tabledrag.js from
+ * being added twice accidently.
+ */
+function theme_tabledrag_example_parent_form($variables) {
+  $form = $variables['form'];
+
+  // Initialize the variable which will store our table rows
+  $rows = array();
+
+  // Iterate over each element in our $form['example_items'] array
+  foreach (element_children($form['example_items']) as $id) {
+
+    // Before we add our 'weight' column to the row, we need to give the
+    // element a custom class so that it can be identified in the
+    // drupal_add_tabledrag call.
+    //
+    // This could also have been done during the form declaration by adding
+    //     '#attributes' => array('class' => 'example-item-weight'),
+    // directly to the 'weight' element in tabledrag_example_simple_form().
+    $form['example_items'][$id]['weight']['#attributes']['class'] = array('example-item-weight');
+
+    // In the parent/child example, we must also set this same custom class on
+    // our id and parent_id columns (which could also have been done within
+    // the form declaration, as above).
+    $form['example_items'][$id]['id']['#attributes']['class'] = array('example-item-id');
+    $form['example_items'][$id]['pid']['#attributes']['class'] = array('example-item-pid');
+
+    // To support the tabledrag behaviour, we need to assign each row of the
+    // table a class attribute of 'draggable'. This will add the 'draggable'
+    // class to the <tr> element for that row when the final table is
+    // rendered.
+    $class = array('draggable');
+
+    // We can add the 'tabledrag-root' class to a row in order to indicate
+    // that the row may not be nested under a parent row.  In our sample data
+    // for this example, the description for the item with id '8' flags it as
+    // a 'root' item which should not be nested.
+    if ($id == '8') {
+      $class[] = 'tabledrag-root';
+    }
+
+    // We can add the 'tabledrag-leaf' class to a row in order to indicate
+    // that the row may not contain child rows.  In our sample data for this
+    // example, the description for the item with id '9' flags it as a 'leaf'
+    // item which can not contain child items.
+    if ($id == '9') {
+      $class[] = 'tabledrag-leaf';
+    }
+
+    // If this is a child element, we need to add some indentation to the row,
+    // so that it appears nested under its parent.  Our $depth parameter was
+    // calculated while building the tree in tabledrag_example_parent_get_data
+    $indent = theme('indentation', array('size' => $form['example_items'][$id]['depth']['#value']));
+    unset ($form['example_items'][$id]['depth']);
+
+    // We are now ready to add each element of our $form data to the $rows
+    // array, so that they end up as individual table cells when rendered
+    // in the final table.  We run each element through the drupal_render()
+    // function to generate the final html markup for that element.
+    $rows[] = array(
+      'data' => array(
+        // Add our 'name' column, being sure to include our indentation.
+        $indent . drupal_render($form['example_items'][$id]['name']),
+        // Add our 'description' column
+        drupal_render($form['example_items'][$id]['description']),
+        // Add our 'weight' column
+        drupal_render($form['example_items'][$id]['weight']),
+        // Add our hidden 'id' column
+        drupal_render($form['example_items'][$id]['id']),
+        // Add our hidden 'parent id' column
+        drupal_render($form['example_items'][$id]['pid']),
+      ),
+      // To support the tabledrag behaviour, we need to assign each row of the
+      // table a class attribute of 'draggable'. This will add the 'draggable'
+      // class to the <tr> element for that row when the final table is
+      // rendered.
+      'class' => $class,
+    );
+  }
+
+  // We now define the table header values.  Ensure that the 'header' count
+  // matches the final column count for your table.
+  //
+  // Normally, we would hide the headers on our hidden columns, but we are
+  // leaving them visible in this example.
+  // $header = array(t('Name'), t('Description'), '', '', '');
+  $header = array(t('Name'), t('Description'), t('Weight'), t('ID'), t('PID'));
+
+  // We also need to pass the drupal_add_tabledrag() function an id which will
+  // be used to identify the <table> element containing our tabledrag form.
+  // Because an element's 'id' should be unique on a page, make sure the value
+  // you select is NOT the same as the form ID used in your form declaration.
+  $table_id = 'example-items-table';
+
+  // We can render our tabledrag table for output.
+  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id)));
+
+  // And then render any remaining form elements (such as our submit button)
+  $output .= drupal_render_children($form);
+
+  // We now call the drupal_add_tabledrag() function in order to add the
+  // tabledrag.js goodness onto our page.
+  //
+  // For our parent/child tree table, we need to pass it:
+  //   - the $table_id of our <table> element (example-items-table),
+  //   - the $action to be performed on our form items ('match'),
+  //   - a string describing where $action should be applied ('parent'),
+  //   - the $group value (pid column) class name ('example-item-pid'),
+  //   - the $subgroup value (pid column) class name ('example-item-pid'),
+  //   - the $source value (id column) class name ('example-item-id'),
+  //   - an optional $hidden flag identifying if the columns should be hidden,
+  //   - an optional $limit parameter to control the max parenting depth
+  drupal_add_tabledrag($table_id, 'match', 'parent', 'example-item-pid', 'example-item-pid', 'example-item-id', FALSE);
+
+  // Because we also want to sort in addition to providing parenting, we call
+  // the drupal_add_tabledrag function again, instructing it to update the
+  // weight field as items at the same level are re-ordered.
+  drupal_add_tabledrag($table_id, 'order', 'sibling', 'example-item-weight', NULL, NULL, FALSE);
+
+  return $output;
+}
+
+/**
+ * Submit callback for the tabledrag_example_parent_form form
+ *
+ * Updates the 'weight' column for each element in our table, taking into
+ * account that item's new order after the drag and drop actions have been
+ * performed.
+ */
+function tabledrag_example_parent_form_submit($form, &$form_state) {
+  // Because the form elements were keyed with the item ids from the database,
+  // we can simply iterate through the submitted values.
+  foreach ($form_state['values']['example_items'] as $id => $item) {
+    db_query(
+      "UPDATE {tabledrag_example} SET weight = :weight, pid = :pid WHERE id = :id",
+      array(':weight' => $item['weight'], ':pid' => $item['pid'], ':id' => $id)
+    );
+  }
+}
+
+/**
+ * Retrives the tree structure from database, and sorts by parent/child/weight
+ *
+ * The sorting should result in children items immediately following their
+ * parent items, with items at the same level of the hierarchy sorted by
+ * weight.
+ *
+ * The approach used here may be considered too database-intensive.
+ * Optimization of the approach is left as an exercise for the reader. :)
+ */
+function tabledrag_example_parent_get_data() {
+  // Get all 'root node' items (items with no parents), sorted by weight.
+  $rootnodes = db_query('SELECT id, name, description, weight, pid
+                       FROM {tabledrag_example}
+                       WHERE (pid = 0)
+                       ORDER BY weight ASC');
+  // Initialize a variable to store our ordered tree structure
+  $itemtree = array();
+  // Depth will be incremented in our _get_tree() function for the first
+  // parent item, so we start it at -1.
+  $depth = -1;
+  // Loop through the root nodes, and add their trees to the array.
+  foreach ($rootnodes as $parent) {
+    tabledrag_example_get_tree($parent, $itemtree, $depth);
+  }
+  return $itemtree;
+}
+
+/**
+ * Recursively adds to the $itemtree array, ordered by parent/child/weight
+ */
+function tabledrag_example_get_tree($parentitem, &$itemtree = array(), &$depth = 0) {
+  // Increase our $depth value by one
+  $depth++;
+  // Set the current tree 'depth' for this item, used to calculate indentation
+  $parentitem->depth = $depth;
+  // Add the parent item to the tree
+  $itemtree[$parentitem->id] = $parentitem;
+  // Retrieve each of the children belonging to this parent
+  $children = db_query('SELECT id, name, description, weight, pid
+                      FROM {tabledrag_example}
+                      WHERE (pid = :pid)
+                      ORDER BY weight ASC',
+                      array(':pid' => $parentitem->id));
+  foreach ($children as $child) {
+    // Make sure this child does not already exist in the tree, to avoid loops
+    if (!in_array($child->id, array_keys($itemtree))) {
+      // Add this child's tree to the $itemtree array.
+      tabledrag_example_get_tree($child, $itemtree, $depth);
+    }
+  }
+  // Finished processing this tree branch.  Decrease our $depth value by one
+  // to represent moving to the next branch.
+  $depth--;
+  // Return
+  return;
+}
diff --git a/tabledrag_example/tabledrag_example_simple_form.inc b/tabledrag_example/tabledrag_example_simple_form.inc
new file mode 100644
index 0000000..8c2a17a
--- /dev/null
+++ b/tabledrag_example/tabledrag_example_simple_form.inc
@@ -0,0 +1,167 @@
+<?php
+/**
+ * @file
+ * Example demonstrating a simple (i.e. 'sort' only) tabledrag form
+ */
+
+/**
+ * Build the tabledrag_simple_example_form form
+ *
+ * @return
+ *   A form array set for theming by theme_tabledrag_example_simple_form()
+ */
+function tabledrag_example_simple_form($form_state) {
+  // Identify that the elements in 'example_items' are a collection, to
+  // prevent Form API from flattening the array when submitted.
+  $form['example_items']['#tree'] = TRUE;
+
+  // Fetch the example data from the database, ordered by weight ascending.
+  //
+  // This query excludes the last two tabledrag_example database rows, as
+  // they are intended only for the 'parent/child' tabledrag examples.
+  $result = db_query('SELECT id, name, description, weight FROM {tabledrag_example} WHERE id < 8 ORDER BY weight ASC');
+
+  // Iterate through each database result
+  foreach ($result as $item) {
+
+    // Create a form entry for this item.
+    //
+    // Each entry will be an array using the the unique id for that item as
+    // the array key, and an array of table row data as the value.
+    $form['example_items'][$item->id] = array(
+
+      // We'll use a form element of type '#markup' to display the item name.
+      'name' => array(
+        '#markup' => check_plain($item->name),
+      ),
+
+      // We'll use a form element of type '#textfield' to display the item
+      // description, which will allow the value to be changed via the form.
+      // We limit the input to 255 characters, which is the limit we set on
+      // the database field.
+      'description' => array(
+        '#type' => 'textfield',
+        '#default_value' => check_plain($item->description),
+        '#size' => 20,
+        '#maxlength' => 255,
+      ),
+
+      // The 'weight' field will be manipulated as we move the items around in
+      // the table using the tabledrag activity.  We use the 'weight' element
+      // defined in Drupal's Form API.
+      'weight' => array(
+        '#type' => 'weight',
+        '#title' => t('Weight'),
+        '#default_value' => $item->weight,
+        '#delta' => 10,
+        '#title-display' => 'invisible',
+      ),
+    );
+  }
+
+  // Now we add our submit button, for submitting the form results.
+  //
+  // The 'actions' wrapper used here isn't strictly necessary for tabledrag,
+  // but is included as a Form API recommended practice.
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array( '#type' => 'submit', '#value' => t('Save Changes'));
+  return $form;
+}
+
+/**
+ * Theme callback for the tabledrag_example_simple_form form
+ *
+ * The theme callback will format the $form data structure into a table and
+ * add our tabledrag functionality.  (Note that drupal_add_tabledrag should be
+ * called from the theme layer, and not from a form declaration.  This helps
+ * keep template files clean and readable, and prevents tabledrag.js from
+ * being added twice accidently.
+ *
+ * @return
+ *   The rendered tabledrag form
+ */
+function theme_tabledrag_example_simple_form($variables) {
+  $form = $variables['form'];
+
+  // Initialize the variable which will store our table rows
+  $rows = array();
+
+  // Iterate over each element in our $form['example_items'] array
+  foreach (element_children($form['example_items']) as $id) {
+
+    // Before we add our 'weight' column to the row, we need to give the
+    // element a custom class so that it can be identified in the
+    // drupal_add_tabledrag call.
+    //
+    // This could also have been done during the form declaration by adding
+    //     '#attributes' => array('class' => 'example-item-weight'),
+    // directy to the 'weight' element in tabledrag_example_simple_form().
+    $form['example_items'][$id]['weight']['#attributes']['class'] = array('example-item-weight');
+
+    // We are now ready to add each element of our $form data to the $rows
+    // array, so that they end up as individual table cells when rendered
+    // in the final table.  We run each element through the drupal_render()
+    // function to generate the final html markup for that element.
+    $rows[] = array(
+      'data' => array(
+        // Add our 'name' column
+        drupal_render($form['example_items'][$id]['name']),
+        // Add our 'description' column
+        drupal_render($form['example_items'][$id]['description']),
+        // Add our 'weight' column
+        drupal_render($form['example_items'][$id]['weight']),
+      ),
+      // To support the tabledrag behaviour, we need to assign each row of the
+      // table a class attribute of 'draggable'. This will add the 'draggable'
+      // class to the <tr> element for that row when the final table is
+      // rendered.
+      'class' => array('draggable'),
+    );
+  }
+
+  // We now define the table header values.  Ensure that the 'header' count
+  // matches the final column count for your table.
+  $header = array(t('Name'), t('Description'), t('Weight'));
+
+  // We also need to pass the drupal_add_tabledrag() function an id which will
+  // be used to identify the <table> element containing our tabledrag form.
+  // Because an element's 'id' should be unique on a page, make sure the value
+  // you select is NOT the same as the form ID used in your form declaration.
+  $table_id = 'example-items-table';
+
+  // We can render our tabledrag table for output.
+  $output = theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $table_id)));
+
+  // And then render any remaining form elements (such as our submit button)
+  $output .= drupal_render_children($form);
+
+  // We now call the drupal_add_tabledrag() function in order to add the
+  // tabledrag.js goodness onto our page.
+  //
+  // For a basic sortable table, we need to pass it:
+  //   - the $table_id of our <table> element,
+  //   - the $action to be performed on our form items ('order'),
+  //   - a string describing where $action should be applied ('siblings'),
+  //   - and the class of the element containing our 'weight' element.
+  drupal_add_tabledrag($table_id, 'order', 'sibling', 'example-item-weight');
+
+  return $output;
+}
+
+/**
+ * Submit callback for the tabledrag_example_simple_form form
+ *
+ * Updates the 'weight' column for each element in our table, taking into
+ * account that item's new order after the drag and drop actions have been
+ * performed.
+ */
+function tabledrag_example_simple_form_submit($form, &$form_state) {
+  // Because the form elements were keyed with the item ids from the database,
+  // we can simply iterate through the submitted values.
+  foreach ($form_state['values']['example_items'] as $id => $item) {
+    db_query(
+      "UPDATE {tabledrag_example} SET weight = :weight WHERE id = :id",
+      array(':weight' => $item['weight'], ':id' => $id)
+    );
+  }
+}
