diff --git a/Annotation/Component.php b/Annotation/Component.php
new file mode 100644
index 0000000..d92f46b
--- /dev/null
+++ b/Annotation/Component.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Annotation\Component.
+ */
+
+namespace Drupal\webform\Annotation;
+
+use Drupal\Component\Annotation\Plugin;
+
+/**
+ * Defines a Component item annotation object.
+ *
+ * Plugin Namespace: Plugin\webform\component
+ *
+ * @see \Drupal\webform\Plugin\ComponentManager
+ * @see plugin_api
+ *
+ * @Annotation
+ */
+class Component extends Plugin {
+
+  /**
+   * The plugin ID.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The name of the component.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $label;
+
+  /**
+   * The description of the component.
+   *
+   * @var \Drupal\Core\Annotation\Translation
+   *
+   * @ingroup plugin_translatable
+   */
+  public $description;
+
+}
diff --git a/src/ComponentBase.php b/src/ComponentBase.php
new file mode 100644
index 0000000..9d12a27
--- /dev/null
+++ b/src/ComponentBase.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Provides Drupal\webform\ComponentBase.
+ */
+
+namespace Drupal\webform;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Form\FormStateInterface;
+
+class ComponentBase extends PluginBase implements ComponentInterface {
+
+  public function getLabel() {
+    return $this->pluginDefinition['label'];
+  }
+
+  public function getDescription() {
+    return $this->pluginDefinition['description'];
+  }
+
+  public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
+
+  }
+}
diff --git a/src/ComponentInterface.php b/src/ComponentInterface.php
new file mode 100644
index 0000000..53f6f51
--- /dev/null
+++ b/src/ComponentInterface.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Provides Drupal\webform\ComponentInterface
+ */
+
+namespace Drupal\webform;
+
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
+/**
+ * Defines an interface for webform component plugins.
+ */
+interface ComponentInterface extends PluginInspectionInterface {
+  /**
+   * Return the label of the component.
+   *
+   * @return string
+   */
+  public function getLabel();
+
+  /**
+   * Return the description of the component.
+   *
+   * @return string
+   */
+  public function getDescription();
+
+}
diff --git a/src/ComponentManager.php b/src/ComponentManager.php
new file mode 100644
index 0000000..feacb68
--- /dev/null
+++ b/src/ComponentManager.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Contains ComponentManager.
+ */
+
+namespace Drupal\webform;
+
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Component plugin manager.
+ */
+class ComponentManager extends DefaultPluginManager {
+
+  /**
+   * Constructs the ComponentManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler to invoke the alter hook with.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/Component', $namespaces, $module_handler, 'Drupal\webform\ComponentInterface', 'Drupal\webform\Annotation\Component');
+
+    $this->alterInfo('webform_component_info');
+    $this->setCacheBackend($cache_backend, 'webform_component');
+  }
+
+  /**
+   * Returns a list of component plugins.
+   *
+   * @return array
+   *   List of component plugins keyed by ID.
+   */
+  public function componentList() {
+    $component_list = array();
+    $component_types = $this->getDefinitions();
+    foreach ($component_types as $key => $component_type) {
+      $component = $this->createInstance($component_type['id']);
+      $component_list[$key] = $component->getLabel();
+    }
+
+    return $component_list;
+  }
+
+}
diff --git a/src/Form/WebformComponentAddForm.php b/src/Form/WebformComponentAddForm.php
new file mode 100644
index 0000000..dff01ba
--- /dev/null
+++ b/src/Form/WebformComponentAddForm.php
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Form\WebformComponentAddForm.
+ */
+
+namespace Drupal\webform\Form;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\FormBase;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\webform\ComponentManager;
+
+/**
+ * Add / edit components on a webform.
+ */
+class WebformComponentAddForm extends FormBase {
+
+  /**
+   * The component manager service.
+   *
+   * @var \Drupal\webform\ComponentManager
+   */
+  protected $componentManager;
+
+  /**
+   * Constructs a WebformComponentsForm object.
+   *
+   * @param ComponentManager $component_manager
+   *   The component manager service.
+   */
+  public function __construct(ComponentManager $component_manager) {
+    $this->componentManager = $component_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.webform.component')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'webform_component_add';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state, $node = NULL, $component = NULL) {
+    // Load the component plugin.
+    $component = $this->componentManager->createInstance($component);
+
+    // Get the form from the component plugin.
+    $form = $component->buildForm($form, $form_state, $node);
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    $values = $form_state->getValues();
+
+    //parent::submitForm($form, $form_state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save(array $form, FormStateInterface $form_state) {
+    // parent::save($form, $form_state);
+    drupal_set_message($this->t('Changes to the webform have been saved.'));
+  }
+
+}
diff --git a/src/Form/WebformComponentsForm.php b/src/Form/WebformComponentsForm.php
index e12df2a..c7ad197 100644
--- a/src/Form/WebformComponentsForm.php
+++ b/src/Form/WebformComponentsForm.php
@@ -7,471 +7,285 @@
 
 namespace Drupal\webform\Form;
 
-use Drupal\Component\Utility\SafeMarkup;
-use Drupal\Component\Utility\Unicode;
-use Drupal\Component\Utility\Xss;
-use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Url;
-use Drupal\node\Entity\Node;
-use Drupal\node\NodeInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Drupal\webform\ComponentManager;
 
 /**
- * Provides a table-based listing of all components for a webform.
+ * Add / edit components on a webform.
  */
 class WebformComponentsForm extends FormBase {
 
   /**
+   * The image effect manager service.
+   *
+   * @var \Drupal\image\ImageEffectManager
+   */
+  protected $componentManager;
+
+  /**
+   * The ID of the node this form is attached to.
+   *
+   * @var $nodeId
+   */
+  protected $nodeId;
+
+  /**
+   * Constructs a WebformComponentsForm object.
+   *
+   * @param \Drupal\image\ImageEffectManager $image_effect_manager
+   *   The image effect manager service.
+   */
+  public function __construct(ComponentManager $component_manager) {
+    $this->componentManager = $component_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('plugin.manager.webform.component')
+    );
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getFormId() {
-    return 'webform_components_form';
+    return 'webform_components';
   }
 
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node = NULL) {
-    $form = [
-      '#tree' => TRUE,
-      '#node' => $node,
-      '#component_options' => webform_component_options(),
-      '#component_weights' => [],
-      'components' => [],
-    ];
-
-    $form['nid'] = [
-      '#type' => 'value',
-      '#value' => $node->id(),
-    ];
-
-    $form['components'] = [
+  public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
+    // @TODO: Placeholder data. Need to load the existing components.
+    $components[1] = array(
+      'cid' => 1,
+      'pid' => 0,
+      'form_key' => 'textfield1',
+      'name' => 'Textfield 1',
+      'type' => 'textfield',
+      'value' => 'TextFieldDefault',
+      'required' => 0,
+      'weight' => 1,
+    );
+
+    $components[2] = array(
+      'cid' => 2,
+      'pid' => 0,
+      'form_key' => 'textfield2',
+      'name' => 'Textfield 2',
+      'type' => 'textfield',
+      'value' => 'TextField2Default',
+      'required' => 0,
+      'weight' => 2,
+    );
+
+    $components[3] = array(
+      'cid' => 3,
+      'pid' => 0,
+      'form_key' => 'date1',
+      'name' => 'Date 1',
+      'type' => 'date',
+      'value' => 'Date Default',
+      'required' => 0,
+      'weight' => 3,
+    );
+
+    $this->nodeId = $node;
+    $user_input = $form_state->getUserInput();
+
+    $form['components'] = array(
       '#type' => 'table',
-      '#header' => [
-        $this->t('Label'),
-        $this->t('Type'),
-        $this->t('Value'),
-        $this->t('Required'),
-        $this->t('Weight'),
-        $this->t('Parent'),
-        $this->t('Operations'),
-      ],
-      '#tabledrag' => [
-        [
-          'action' => 'match',
-          'relationship' => 'parent',
-          'group' => 'webform-pid',
-          'subgroup' => 'webform-pid',
-          'source' => 'webform-cid',
-          'hidden' => TRUE,
-        ],
-        [
+      '#header' => array(
+        t('Label'),
+        t('Type'),
+        t('Value'),
+        t('Required'),
+        t('Weight'),
+        t('Operations')
+      ),
+      '#empty' => t('There are no components yet.'),
+      '#tabledrag' => array(
+        array(
           'action' => 'order',
           'relationship' => 'sibling',
-          'group' => 'webform-weight',
-        ],
-      ],
-      '#attributes' => [
-        'id' => 'webform-components',
-      ],
-      '#attached' => [
-        'library' => [
-          'webform/webform.admin',
-        ],
-      ]
-    ];
-
-    // Get max weight and set default weights for all components. Defaults
-    // needed to adjust later if the add component form needs to be inserted
-    // directly after newly added component.
-    foreach ($node->webform['components'] as $cid => $component) {
-      $form['#component_weights'][$cid] = $component['weight'];
-      if (!isset($max_weight) || $component['weight'] > $max_weight) {
-        $max_weight = $component['weight'];
-      }
+          'group' => 'components-order-weight',
+        ),
+      ),
+      '#attributes' => array(
+        'id' => 'components',
+      ),
+    );
+
+    foreach ($components as $id => $component) {
+      $form['components'][$id]['#attributes']['class'][] = 'draggable';
+      $form['components'][$id]['#weight'] = isset($user_input['components']) ? $user_input['components'][$id]['weight'] : NULL;
+
+      $form['components'][$id]['label'] = array(
+        '#tree' => FALSE,
+        'data' => array(
+          'label' => array(
+            '#markup' => $component['name'],
+          ),
+        ),
+      );
+
+      $form['components'][$id]['type'] = array(
+        '#tree' => FALSE,
+        'data' => array(
+          'label' => array(
+            '#markup' => $component['type'],
+          ),
+        ),
+      );
+
+      $form['components'][$id]['value'] = array(
+        '#tree' => FALSE,
+        'data' => array(
+          'label' => array(
+            '#markup' => $component['value'],
+          ),
+        ),
+      );
+
+      $form['components'][$id]['required'] = array(
+        '#type' => 'checkbox',
+      );
+
+      $form['components'][$id]['weight'] = array(
+        '#type' => 'weight',
+        '#title' => t('Weight for @title', array('@title' => $components['name'])),
+        '#title_display' => 'invisible',
+        '#default_value' => $components['weight'],
+        '#attributes' => array('class' => array('components-order-weight')),
+      );
+
+      $form['components'][$id]['operations'] = array(
+        '#type' => 'operations',
+        '#links' => array(),
+      );
+      $form['components'][$id]['operations']['#links']['edit'] = array(
+        'title' => t('Edit'),
+        'url' => Url::fromRoute('webform.component_edit_form', array('id' => $id)),
+      );
+      $form['components'][$id]['operations']['#links']['delete'] = array(
+        'title' => t('Delete'),
+        'url' => Url::fromRoute('webform.component_delete_form', array('id' => $id)),
+      );
     }
 
-    // Create an add form.
-    $add_form = [
-      '#attributes' => [
-        'class' => ['draggable', 'webform-add-form', 'tabledrag-leaf'],
-      ]
-    ];
-    $add_form['name'] = [
+    // Fields for adding a new component.
+    // @TODO: Label/required are not passed to add form. Either pass them or remove them from this form.
+    $form['components']['new'] = array(
+      '#tree' => FALSE,
+      '#weight' => isset($user_input['weight']) ? $user_input['weight'] : NULL,
+      '#attributes' => array('class' => array('draggable')),
+    );
+
+    $form['components']['new']['label'] = array(
       '#type' => 'textfield',
-      '#title' => $this->t('New component name'),
-      '#title_display' => 'invisible',
       '#size' => 24,
       '#maxlength' => NULL,
-      '#attributes' => [
-        'class' => ['webform-component-name'],
-        'placeholder' => $this->t('New component name'),
-      ],
-    ];
-    $add_form['type'] = [
-      '#type' => 'select',
-      '#options' => $form['#component_options'],
-      '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['type'] : 'textfield',
-      '#attributes' => [
-        'class' => ['webform-component-type'],
-      ],
-    ];
-    $add_form['value'] = [
-      '#markup' => '',
-      '#attributes' => [
-        'class' => ['webform-component-value'],
-      ],
-    ];
-    $add_form['required'] = [
+    );
+
+    $form['components']['new']['type'] = array(
+      'data' => array(
+        'new' => array(
+          '#type' => 'select',
+          '#title' => $this->t('Component'),
+          '#title_display' => 'invisible',
+          '#options' => $this->componentManager->componentList(),
+          '#empty_option' => $this->t('Select type'),
+        ),
+      ),
+      '#prefix' => '<div class="component-new">',
+      '#suffix' => '</div>',
+    );
+    $form['components']['new']['value'] = array(
+      'data' => array(),
+    );
+    $form['components']['new']['required'] = array(
       '#type' => 'checkbox',
-      '#title' => $this->t('Required'),
-      '#title_display' => 'invisible',
-      '#attributes' => [
-        'class' => ['webform-component-required'],
-      ],
-    ];
-    $add_form['weight'] = [
-      '#type' => 'textfield',
+    );
+
+    $form['components']['new']['weight'] = array(
+      '#type' => 'weight',
       '#title' => $this->t('Weight for new component'),
       '#title_display' => 'invisible',
-      '#size' => 4,
-      '#delta' => count($node->webform['components']) > 10 ? count($node->webform['components']) : 10,
-      '#attributes' => [
-        'class' => ['webform-weight'],
-      ],
-    ];
-
-    if (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) {
-      // Make the add form appear by default directly after the one that was
-      // just added.
-      $add_form['weight']['#default_value'] = $form['#component_weights'][$_GET['cid']]['weight'] + 1;
-      foreach (array_keys($node->webform['components']) as $cid) {
-        // Adjust all later components also, to make sure none of them have the
-        // same weight as the new component.
-        if ($form['#component_weights'][$cid] >= $add_form['weight']['#default_value']) {
-          $form['#component_weights'][$cid]++;
-        }
-      }
-    }
-    else {
-      // If no component was just added, the new component should appear by
-      // default at the end of the list.
-      $add_form['weight']['#default_value'] = isset($max_weight) ? $max_weight + 1 : 0;
-    }
-
-    $add_form['parent']['cid'] = [
-      '#parents' => ['components', 'add', 'cid'],
-      '#type' => 'hidden',
-      '#default_value' => '',
-      '#attributes' => [
-        'class' => ['webform-cid'],
-      ],
-    ];
-    $add_form['parent']['pid'] = [
-      '#parents' => ['components', 'add', 'pid'],
-      '#type' => 'hidden',
-      '#default_value' => (isset($_GET['cid']) && isset($node->webform['components'][$_GET['cid']])) ? $node->webform['components'][$_GET['cid']]['pid'] : 0,
-      '#attributes' => [
-        'class' => ['webform-pid'],
-      ],
-    ];
-    $add_form['add'] = [
-      '#type' => 'submit',
-      '#value' => $this->t('Add'),
-      '#validate' => ['::validateComponentAddForm', '::validateComponentsForm'],
-      '#submit' => ['::submitComponentAddForm'],
-      '#wrapper_attributes' => [
-        'class' => ['webform-component-add'],
-      ],
-    ];
-
-    // Output all existing components.
-    if (!empty($node->webform['components'])) {
-      $component_tree = [];
-      $page_count = 1;
-      _webform_components_tree_build($node->webform['components'], $component_tree, 0, $page_count);
-      $component_tree = _webform_components_tree_sort($component_tree);
-      // Build the table rows recursively.
-      foreach ($component_tree['children'] as $cid => $component) {
-        $this->buildComponentsTableRow($node, $cid, $component, 0, $form, $add_form);
-      }
-    }
-    else {
-      $form['components'][] = [
-        [
-          '#markup' => $this->t('No Components, add a component below.'),
-          '#wrapper_attributes' => [
-            'colspan' => 7,
-          ],
-        ]
-      ];
-    }
-
-    // Append the add form if not already output.
-    if ($add_form) {
-      $form['components']['add'] = $add_form;
-    }
-
-    $form['actions'] = [
-      '#type' => 'actions',
-    ];
-    $form['actions']['submit'] = [
+      '#default_value' => count($components) + 1,
+      '#attributes' => array('class' => array('components-order-weight')),
+    );
+
+    $form['components']['new']['operations'] = array(
+      'data' => array(
+        'add' => array(
+          '#type' => 'submit',
+          '#value' => $this->t('Add'),
+          '#validate' => array('::componentValidate'),
+          '#submit' => array('::submitForm', '::componentSave'),
+        ),
+      ),
+    );
+
+    $form['actions'] = array('#type' => 'actions');
+    $form['actions']['submit'] = array(
       '#type' => 'submit',
-      '#value' => $this->t('Save'),
-      '#access' => count($node->webform['components']) > 0,
-      '#validate' => ['::validateComponentsForm'],
-    ];
-
-    $form['warning'] = [
-      '#weight' => -1,
-    ];
-    webform_input_vars_check($form, $form_state, 'components', 'warning');
+      '#value' => t('Save changes'),
+    );
 
     return $form;
   }
 
   /**
-   * Form validation handler for adding a new component.
+   * Validate handler for component.
    */
-  public function validateComponentAddForm($form, &$form_state) {
-    // Check that the entered component name is valid.
-    if (Unicode::strlen(trim($form_state->getValue(['components', 'add', 'name']))) <= 0) {
-      $form_state->setErrorByName('components][add][name', $this->t('When adding a new component, the name field is required.'));
+  public function componentValidate($form, FormStateInterface $form_state) {
+    if (!$form_state->getValue('new')) {
+      $form_state->setErrorByName('new', $this->t('Select a component to add.'));
     }
   }
 
   /**
-   * Form validation handler for updating components.
+   * Submit handler for component.
    */
-  public function validateComponentsForm($form, &$form_state) {
-    // Check that no two components end up with the same form key.
-    $duplicates = [];
-    $parents = [];
-    $components = $form_state->getValue('components');
-    unset($components['add']);
-    if ($components) {
-      foreach ($components as $cid => $component) {
-        $form_key = $form['#node']->webform['components'][$cid]['form_key'];
-        if (isset($parents[$component['pid']]) && ($existing = array_search($form_key, $parents[$component['pid']])) && $existing !== FALSE) {
-          if (!isset($duplicates[$form_key])) {
-            $duplicates[$form_key] = [$existing];
-          }
-          $duplicates[$form_key][] = $cid;
-        }
-        $parents[$component['pid']][$cid] = $form_key;
-      }
-    }
+  public function componentSave($form, FormStateInterface $form_state) {
+    $this->save($form, $form_state);
+
+    $component_id = $form_state->getValue('new');
+
+    // Load the configuration form for this option.
+    $form_state->setRedirect(
+      'webform.component_add_form',
+      array(
+        'node' => $this->nodeId,
+        'component' => $component_id,
+      ),
+      array('query' => array('weight' => $form_state->getValue('weight')))
+    );
 
-    if (!empty($duplicates)) {
-      $items = [];
-      foreach ($duplicates as $form_key => $cids) {
-        foreach ($cids as $cid) {
-          $items[] = webform_filter_xss($form['#node']->webform['components'][$cid]['name']);
-        }
-      }
-      $list = [
-        '#theme' => 'item_list',
-        '#items' => $items,
-      ];
-
-      $form_state->setErrorByName('', $this->t('The form order failed to save because the following elements have same form keys and are under the same parent. Edit each component and give them a unique form key, then try moving them again. !list_components', ['!list_components' => drupal_render($list)]));
-    }
   }
 
   /**
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
-    $node = Node::load($form_state->getValue('nid'));
-
-    // Update all weight, required, and pid values.
-    $changes = FALSE;
-    foreach ($node->webform['components'] as $cid => $component) {
-      if ($component['pid'] != $form_state->getValue(['components', $cid, 'pid']) || $component['weight'] != $form_state->getValue(['components', $cid, 'weight']) || $component['required'] != $form_state->getValue(['components', $cid, 'required'])) {
-        $changes = TRUE;
-        $node->webform['components'][$cid]['weight'] = $form_state->getValue(['components', $cid, 'weight']);
-        $node->webform['components'][$cid]['required'] = $form_state->getValue(['components', $cid, 'required']);
-        $node->webform['components'][$cid]['pid'] = $form_state->getValue(['components', $cid, 'pid']);
-      }
-    }
-
-    if ($changes) {
-      $node->save();
-    }
+    $values = $form_state->getValues();
 
-    drupal_set_message($this->t('The component positions and required values have been updated.'));
-  }
-
-  /**
-   * Form submission handler to redirect to the new component form.
-   *
-   * @param array $form
-   *   An associative array containing the structure of the form.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
-   */
-  public function submitComponentAddForm(array &$form, FormStateInterface $form_state) {
-    $node = Node::load($form_state->getValue('nid'));
-
-    $component = $form_state->getValue(['components', 'add']);
-
-    // Set the values in the query string for the add component page.
-    $query = [
-      'name' => $component['name'],
-      'required' => $component['required'],
-      'pid' => $component['pid'],
-      'weight' => $component['weight'],
-    ];
-
-    /* @todo Set the destination when the route exists.
-    // Forward the "destination" query string value to the next form.
-    if (isset($_GET['destination'])) {
-      $query['destination'] = $_GET['destination'];
-      unset($_GET['destination']);
-      drupal_static_reset('drupal_get_destination');
-    }
-    $form_state['redirect'] = ['node/' . $node->id() . '/webform/components/new/' . $component['type'], ['query' => $query]];
-    */
+    //parent::submitForm($form, $form_state);
   }
 
   /**
-   * Helper to recursively build table rows to hold existing components.
-   *
-   * @param object $node
-   *   A node object the components belong to.
-   * @param int $cid
-   *   A cid of the component.
-   * @param array $component
-   *   A component.
-   * @param int $level
-   *   The nesting level of this component.
-   * @param array $form
-   *   The form that is being modified, passed by reference.
-   * @param array $add_form
-   *   The add form which will be inserted under any previously added/edited
-   *   component.
-   *
-   * @see self::buildForm()
+   * {@inheritdoc}
    */
-  protected function buildComponentsTableRow($node, $cid, $component, $level, &$form, &$add_form) {
-    $row_class = ['draggable'];
-    if (!webform_component_feature($component['type'], 'group')) {
-      $row_class[] = 'tabledrag-leaf';
-    }
-    if ($component['type'] == 'pagebreak') {
-      $row_class[] = 'tabledrag-root';
-      $row_class[] = 'webform-pagebreak';
-    }
-    $form['components'][$cid]['#attributes']['class'] = $row_class;
-    $form['components'][$cid]['#attributes']['data-cid'] = $cid;
-
-    $indentation = '';
-    if ($level >= 1) {
-      $indentation = [
-        '#theme' => 'indentation',
-        '#size' => $level,
-      ];
-      $indentation = drupal_render($indentation);
-    }
-    $form['components'][$cid]['name'] = [
-      '#prefix' => $indentation,
-      '#markup' => Xss::filter($component['name']),
-      '#attributes' => [
-        'class' => ['webform-component-name', $component['type'] == 'pagebreak' ? 'webform-pagebreak' : ''],
-      ],
-    ];
-
-    $form['components'][$cid]['type'] = [
-      '#markup' => $form['#component_options'][$component['type']],
-      '#attributes' => [
-        'class' => ['webform-component-type'],
-      ],
-    ];
-
-    // Create a presentable value.
-    if (Unicode::strlen($component['value']) > 30) {
-      $component['value'] = Unicode::substr($component['value'], 0, 30);
-      $component['value'] .= '...';
-    }
-    $component['value'] = SafeMarkup::checkPlain($component['value']);
-    $form['components'][$cid]['value'] = [
-      '#markup' => ($component['value'] == '') ? '-' : $component['value'],
-      '#attributes' => [
-        'class' => ['webform-component-value'],
-      ],
-    ];
-
-    $form['components'][$cid]['required'] = [
-      '#type' => 'checkbox',
-      '#title' => $this->t('Required'),
-      '#title_display' => 'invisible',
-      '#default_value' => $component['required'],
-      '#access' => webform_component_feature($component['type'], 'required'),
-      '#attributes' => [
-        'class' => ['webform-component-required'],
-      ],
-    ];
-
-    $form['components'][$cid]['weight'] = [
-      '#type' => 'textfield',
-      '#title' => $this->t('Weight for @title', ['@title' => $component['name']]),
-      '#title_display' => 'invisible',
-      '#size' => 4,
-      '#delta' => count($node->webform['components']) > 10 ? count($node->webform['components']) : 10,
-      '#default_value' => $form['#component_weights'][$cid],
-      '#attributes' => [
-        'class' => ['webform-weight'],
-      ],
-    ];
-
-    $form['components'][$cid]['parent']['cid'] = [
-      '#parents' => ['components', $cid, 'cid'],
-      '#type' => 'hidden',
-      '#default_value' => $component['cid'],
-      '#attributes' => [
-        'class' => ['webform-cid'],
-      ],
-    ];
-
-    $form['components'][$cid]['parent']['pid'] = [
-      '#parents' => ['components', $cid, 'pid'],
-      '#type' => 'hidden',
-      '#default_value' => $component['pid'],
-      '#attributes' => [
-        'class' => ['webform-pid'],
-      ],
-    ];
-
-    $form['components'][$cid]['operations'] = [
-      '#type' => 'operations',
-      '#links' => [],
-    ];
-    // @todo Fix these links once the routes exist.
-    $form['components'][$cid]['operations']['#links']['edit'] = [
-      'title' => $this->t('Edit'),
-      'url' => Url::fromRoute('entity.node.webform', ['node' => $node->id()]),
-    ];
-    $form['components'][$cid]['operations']['#links']['clone'] = [
-      'title' => $this->t('Clone'),
-      'url' => Url::fromRoute('entity.node.webform', ['node' => $node->id()]),
-    ];
-    $form['components'][$cid]['operations']['#links']['delete'] = [
-      'title' => $this->t('Delete'),
-      'url' => Url::fromRoute('entity.node.webform', ['node' => $node->id()]),
-    ];
-
-    if (isset($component['children']) && is_array($component['children'])) {
-      foreach ($component['children'] as $cid => $component) {
-        $this->buildComponentsTableRow($node, $cid, $component, $level + 1, $form, $add_form);
-      }
-    }
-
-    // Add the add form if this was the last edited component.
-    if (isset($_GET['cid']) && $component['cid'] == $_GET['cid'] && $add_form) {
-      $add_form['name']['#prefix'] = $indentation;
-      $form['components']['add'] = $add_form;
-      $add_form = FALSE;
-    }
+  public function save(array $form, FormStateInterface $form_state) {
+    // parent::save($form, $form_state);
+    drupal_set_message($this->t('Changes to the webform have been saved.'));
   }
 
 }
diff --git a/src/Form/WebformSettingsForm.php b/src/Form/WebformSettingsForm.php
index 585b473..4391f50 100644
--- a/src/Form/WebformSettingsForm.php
+++ b/src/Form/WebformSettingsForm.php
@@ -47,14 +47,16 @@ class WebformSettingsForm extends ConfigFormBase {
     );
 
     // Add each component to the form.
-    $component_types = webform_components(TRUE);
-    foreach ($component_types as $key => $component) {
+    $manager = \Drupal::service('plugin.manager.webform.component');
+    $component_types = $manager->getDefinitions();
+    foreach ($component_types as $key => $component_type) {
+      $component = $manager->createInstance($component_type['id']);
       $form['components'][$key] = array(
         '#type' => 'checkbox',
-        '#title' => $component['label'],
-        '#description' => $component['description'],
+        '#title' => $component->getLabel(),
+        '#description' => $component->getDescription(),
         '#return_value' => 1,
-        '#default_value' => $component['enabled'],
+        '#default_value' => 1,//$component['enabled'],
       );
     }
 
diff --git a/src/Plugin/Component/Date.php b/src/Plugin/Component/Date.php
new file mode 100644
index 0000000..e8258f8
--- /dev/null
+++ b/src/Plugin/Component/Date.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Date.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'date' component.
+ *
+ * @Component(
+ *   id = "date",
+ *   label = @Translation("Date"),
+ *   description = @Translation("Presents month, day, and year fields.")
+ * )
+ */
+class Date extends ComponentBase {}
diff --git a/src/Plugin/Component/Email.php b/src/Plugin/Component/Email.php
new file mode 100644
index 0000000..40391f0
--- /dev/null
+++ b/src/Plugin/Component/Email.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Email.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'email' component.
+ *
+ * @Component(
+ *   id = "email",
+ *   label = @Translation("E-mail"),
+ *   description = @Translation("A special textfield that accepts e-mail addresses.")
+ * )
+ */
+class Email extends ComponentBase {}
diff --git a/src/Plugin/Component/Fieldset.php b/src/Plugin/Component/Fieldset.php
new file mode 100644
index 0000000..8927f5e
--- /dev/null
+++ b/src/Plugin/Component/Fieldset.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Fieldset.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'fieldset' component.
+ *
+ * @Component(
+ *   id = "fieldset",
+ *   label = @Translation("Fieldset"),
+ *   description = @Translation("A special component that allows grouping of fields.")
+ * )
+ */
+class Fieldset extends ComponentBase {}
diff --git a/src/Plugin/Component/File.php b/src/Plugin/Component/File.php
new file mode 100644
index 0000000..f29d03c
--- /dev/null
+++ b/src/Plugin/Component/File.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\File.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'file' component.
+ *
+ * @Component(
+ *   id = "file",
+ *   label = @Translation("File"),
+ *   description = @Translation("A validated field that allows both public or private file uploads.")
+ * )
+ */
+class File extends ComponentBase {}
diff --git a/src/Plugin/Component/Grid.php b/src/Plugin/Component/Grid.php
new file mode 100644
index 0000000..9224f37
--- /dev/null
+++ b/src/Plugin/Component/Grid.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Grid.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'grid' component.
+ *
+ * @Component(
+ *   id = "grid",
+ *   label = @Translation("Grid"),
+ *   description = @Translation("A tabular display of questions and radio button
+ *    options.")
+ * )
+ */
+class Grid extends ComponentBase {}
diff --git a/src/Plugin/Component/Hidden.php b/src/Plugin/Component/Hidden.php
new file mode 100644
index 0000000..019870c
--- /dev/null
+++ b/src/Plugin/Component/Hidden.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Hidden.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'hidden' component.
+ *
+ * @Component(
+ *   id = "hidden",
+ *   label = @Translation("Hidden"),
+ *   description = @Translation("A form field that is not shown to end-users.")
+ * )
+ */
+class Hidden extends ComponentBase {}
diff --git a/src/Plugin/Component/Markup.php b/src/Plugin/Component/Markup.php
new file mode 100644
index 0000000..60b906e
--- /dev/null
+++ b/src/Plugin/Component/Markup.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Markup.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'markup' component.
+ *
+ * @Component(
+ *   id = "markup",
+ *   label = @Translation("Markup"),
+ *   description = @Translation("A field to display custom HTML on a form.")
+ * )
+ */
+class Markup extends ComponentBase {}
diff --git a/src/Plugin/Component/Number.php b/src/Plugin/Component/Number.php
new file mode 100644
index 0000000..cb36b37
--- /dev/null
+++ b/src/Plugin/Component/Number.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Number.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'number' component.
+ *
+ * @Component(
+ *   id = "number",
+ *   label = @Translation("Number"),
+ *   description = @Translation("A field that accepts numerical values only.")
+ * )
+ */
+class Number extends ComponentBase {}
diff --git a/src/Plugin/Component/Pagebreak.php b/src/Plugin/Component/Pagebreak.php
new file mode 100644
index 0000000..f97a116
--- /dev/null
+++ b/src/Plugin/Component/Pagebreak.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\PageBreak.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'pagebreak' component.
+ *
+ * @Component(
+ *   id = "pagebreak",
+ *   label = @Translation("PageBreak"),
+ *   description = @Translation("Creates a way to have multi-page forms.")
+ * )
+ */
+class PageBreak extends ComponentBase {}
diff --git a/src/Plugin/Component/SelectOptions.php b/src/Plugin/Component/SelectOptions.php
new file mode 100644
index 0000000..9ee3342
--- /dev/null
+++ b/src/Plugin/Component/SelectOptions.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\SelectOptions.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'selectoptions' component.
+ *
+ * @Component(
+ *   id = "selectoptions",
+ *   label = @Translation("SelectOptions"),
+ *   description = @Translation("A field that displays options in a select box.")
+ * )
+ */
+class SelectOptions extends ComponentBase {}
diff --git a/src/Plugin/Component/TextField.php b/src/Plugin/Component/TextField.php
new file mode 100644
index 0000000..09b178a
--- /dev/null
+++ b/src/Plugin/Component/TextField.php
@@ -0,0 +1,104 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\TextField.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'textfield' component.
+ *
+ * @Component(
+ *   id = "textfield",
+ *   label = @Translation("TextField"),
+ *   description = @Translation("A textfield field.")
+ * )
+ */
+class TextField extends ComponentBase {
+
+  public function buildForm(array $form, FormStateInterface $form_state, $node = NULL) {
+    $component = array();
+    $form = array();
+    $form['value'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Default value'),
+      '#default_value' => $component['value'],
+      '#description' => t('The default value of the field.'),
+      '#size' => 60,
+      '#maxlength' => 1024,
+      '#weight' => 0,
+    );
+    $form['display']['width'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Width'),
+      '#default_value' => $component['extra']['width'],
+      '#description' => t('Width of the textfield.') . ' ' . t('Leaving blank will use the default size.'),
+      '#size' => 5,
+      '#maxlength' => 10,
+      '#weight' => 0,
+      '#parents' => array('extra', 'width'),
+    );
+    $form['display']['placeholder'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Placeholder'),
+      '#default_value' => $component['extra']['placeholder'],
+      '#description' => t('The placeholder will be shown in the field until the user starts entering a value.'),
+      '#weight' => 1,
+      '#parents' => array('extra', 'placeholder'),
+    );
+    $form['display']['field_prefix'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Prefix text placed to the left of the textfield'),
+      '#default_value' => $component['extra']['field_prefix'],
+      '#description' => t('Examples: $, #, -.'),
+      '#size' => 20,
+      '#maxlength' => 127,
+      '#weight' => 2.1,
+      '#parents' => array('extra', 'field_prefix'),
+    );
+    $form['display']['field_suffix'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Postfix text placed to the right of the textfield'),
+      '#default_value' => $component['extra']['field_suffix'],
+      '#description' => t('Examples: lb, kg, %.'),
+      '#size' => 20,
+      '#maxlength' => 127,
+      '#weight' => 2.2,
+      '#parents' => array('extra', 'field_suffix'),
+    );
+    $form['display']['disabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Disabled'),
+      '#return_value' => 1,
+      '#description' => t('Make this field non-editable. Useful for setting an unchangeable default value.'),
+      '#weight' => 11,
+      '#default_value' => $component['extra']['disabled'],
+      '#parents' => array('extra', 'disabled'),
+    );
+    $form['validation']['unique'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Unique'),
+      '#return_value' => 1,
+      '#description' => t('Check that all entered values for this field are unique. The same value is not allowed to be used twice.'),
+      '#weight' => 1,
+      '#default_value' => $component['extra']['unique'],
+      '#parents' => array('extra', 'unique'),
+    );
+    $form['validation']['maxlength'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Maxlength'),
+      '#default_value' => $component['extra']['maxlength'],
+      '#description' => t('Maximum length of the textfield value.'),
+      '#size' => 5,
+      '#maxlength' => 10,
+      '#weight' => 2,
+      '#parents' => array('extra', 'maxlength'),
+    );
+
+    return $form;
+  }
+}
diff --git a/src/Plugin/Component/Textarea.php b/src/Plugin/Component/Textarea.php
new file mode 100644
index 0000000..1152b2a
--- /dev/null
+++ b/src/Plugin/Component/Textarea.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Textarea.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'textarea' component.
+ *
+ * @Component(
+ *   id = "textarea",
+ *   label = @Translation("Textarea"),
+ *   description = @Translation("A textarea field.")
+ * )
+ */
+class Textarea extends ComponentBase {}
diff --git a/src/Plugin/Component/Time.php b/src/Plugin/Component/Time.php
new file mode 100644
index 0000000..6edfbf1
--- /dev/null
+++ b/src/Plugin/Component/Time.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\webform\Plugin\Component\Time.
+ */
+
+namespace Drupal\webform\Plugin\Component;
+
+use Drupal\webform\ComponentBase;
+
+/**
+ * Provides a 'time' component.
+ *
+ * @Component(
+ *   id = "time",
+ *   label = @Translation("Time"),
+ *   description = @Translation("A special textfield that accepts time entries.")
+ * )
+ */
+class Time extends ComponentBase {}
diff --git a/webform.links.task.yml b/webform.links.task.yml
index 813fc72..7a66474 100644
--- a/webform.links.task.yml
+++ b/webform.links.task.yml
@@ -3,6 +3,12 @@ webform.content_overview:
   route_name: webform.content_overview
   base_route: system.admin_content
 
+webform.components:
+  route_name: webform.components
+  base_route: entity.node.canonical
+  title: 'Webform'
+  weight: 50
+
 entity.node.webform:
   title: Webform
   route_name: entity.node.webform
diff --git a/webform.routing.yml b/webform.routing.yml
index ad1307e..2c5b1b3 100644
--- a/webform.routing.yml
+++ b/webform.routing.yml
@@ -13,6 +13,36 @@ webform.settings:
   requirements:
     _permission: 'administer site configuration'
 
+# @TODO: These probably need different permissions.
+# @TODO: This needs to be restricted to only show on webform node types.
+webform.components:
+  path: '/node/{node}/webform'
+  defaults:
+    _form: '\Drupal\webform\Form\WebformComponentsForm'
+  requirements:
+    _permission: 'administer site configuration'
+
+webform.component_add_form:
+  path: '/node/{node}/webform/components/new/{component}'
+  defaults:
+    _form: '\Drupal\webform\Form\WebformComponentAddForm'
+  requirements:
+    _permission: 'administer site configuration'
+
+webform.component_edit_form:
+  path: '/node/{node}/webform/components/edit/{component}'
+  defaults:
+    _form: '\Drupal\webform\Form\WebformComponentEditForm'
+  requirements:
+    _permission: 'administer site configuration'
+
+webform.component_delete_form:
+  path: '/node/{node}/webform/components/delete/{component}'
+  defaults:
+    _form: '\Drupal\webform\Form\WebformComponentDeleteForm'
+  requirements:
+    _permission: 'administer site configuration'
+
 # @todo Only show on webform enabled node types
 entity.node.webform:
   path: '/node/{node}/webform'
diff --git a/webform.services.yml b/webform.services.yml
new file mode 100644
index 0000000..afcbfa2
--- /dev/null
+++ b/webform.services.yml
@@ -0,0 +1,4 @@
+services:
+  plugin.manager.webform.component:
+    class: Drupal\webform\ComponentManager
+    parent: default_plugin_manager
