diff --git a/core/lib/Drupal/Core/Entity/Field/Field.php b/core/lib/Drupal/Core/Entity/Field/Field.php
index 7cf493c..be17552 100644
--- a/core/lib/Drupal/Core/Entity/Field/Field.php
+++ b/core/lib/Drupal/Core/Entity/Field/Field.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Entity\Field;
 
+use Drupal\Core\Entity\Field\FieldDefinition;
 use Drupal\Core\Entity\Field\FieldInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
@@ -37,6 +38,13 @@ class Field extends ItemList implements FieldInterface {
   protected $list = array();
 
   /**
+   * The field definition.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldDefinitionInterface
+   */
+  protected $fieldDefinition;
+
+  /**
    * Overrides TypedData::__construct().
    */
   public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
@@ -52,8 +60,10 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
-    // @todo https://drupal.org/node/1988612
-    return NULL;
+    if (!isset($this->fieldDefinition)) {
+      $this->fieldDefinition = new FieldDefinition($this);
+    }
+    return $this->fieldDefinition;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
new file mode 100644
index 0000000..e48a80d
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\Field\FieldDefinition.
+ */
+
+namespace Drupal\Core\Entity\Field;
+
+use Drupal\Component\Utility\NestedArray;
+
+/**
+ * @todo Document.
+ */
+class FieldDefinition implements FieldDefinitionInterface {
+
+  /**
+   * @todo Document.
+   *
+   * @var \Drupal\Core\Entity\Field\FieldInterface
+   */
+  protected $field;
+
+  /**
+   * @todo Document.
+   *
+   * @var array
+   */
+  protected $definition;
+
+  /**
+   * @todo Document.
+   */
+  public function __construct(FieldInterface $field) {
+    $this->field = $field;
+    $definition = $field->getDefinition();
+    $this->definition = NestedArray::mergeDeep(\Drupal::typedData()->getDefinition($definition['type']), $definition);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldName() {
+    return $this->field->getName();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldType() {
+    return $this->definition['field_type'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldSettings() {
+    return $this->definition['settings'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldSetting($setting_name) {
+    return $this->definition['settings'][$setting_name];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldPropertyNames() {
+    return array_keys($this->field->getPropertyDefinitions());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldTranslatable() {
+    return !empty($this->definition['translatable']);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldLabel() {
+    return $this->definition['label'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldDescription() {
+    return $this->definition['description'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFieldCardinality() {
+    return isset($this->definition['cardinality']) ? $this->definition['cardinality'] : 1;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isFieldRequired() {
+    return !empty($this->definition['required']);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php
index 60b47f9..4c1d1d1 100644
--- a/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php
+++ b/core/lib/Drupal/Core/Entity/Plugin/DataType/Deriver/FieldItemDeriver.php
@@ -77,6 +77,16 @@ public function getDerivativeDefinition($derivative_id, array $base_plugin_defin
    */
   public function getDerivativeDefinitions(array $base_plugin_definition) {
     foreach ($this->fieldTypePluginManager->getDefinitions() as $plugin_id => $definition) {
+      // Provide easy access to the field type without requiring consuming code
+      // to parse it from the full data type.
+      $definition['field_type'] = $plugin_id;
+
+      // The distinction between 'settings' and 'instance_settings' is only
+      // meaningful at the field type plugin level. At the Typed data API level,
+      // merge them.
+      $definition['settings'] = $definition['instance_settings'] + $definition['settings'];
+      unset($definition['instance_settings']);
+
       $this->derivatives[$plugin_id] = $definition;
     }
     return $this->derivatives;
diff --git a/core/modules/edit/js/editors/directEditor.js b/core/modules/edit/js/editors/directEditor.js
index 5fed240..59bb6d8 100644
--- a/core/modules/edit/js/editors/directEditor.js
+++ b/core/modules/edit/js/editors/directEditor.js
@@ -21,7 +21,13 @@ Drupal.edit.editors.direct = Drupal.edit.EditorView.extend({
     var fieldModel = this.fieldModel;
 
     // Store the original value of this field. Necessary for reverting changes.
-    var $textElement = this.$textElement = this.$el.find('.field-item:first');
+    var $textElement;
+    if (this.$el.is(':has(.field-item)')) {
+      $textElement = this.$textElement = this.$el.find('.field-item:first');
+    }
+    else {
+     $textElement = this.$textElement = this.$el;
+    }
     editorModel.set('originalValue', $.trim(this.$textElement.text()));
 
     // Sets the state to 'changed' whenever the value changes
diff --git a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
index 9559be7..8e204dd 100644
--- a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
+++ b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php
@@ -42,7 +42,13 @@ public function access(Route $route, Request $request) {
    * {@inheritdoc}
    */
   public function accessEditEntityField(EntityInterface $entity, $field_name) {
-    return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
+    $property_definitions = $entity->getPropertyDefinitions();
+    if (isset($property_definitions[$field_name]['configurable']) && $property_definitions[$field_name]['configurable'] === TRUE) {
+      return $entity->access('update') && ($field = field_info_field($field_name)) && field_access('edit', $field, $entity->entityType(), $entity);
+    }
+    else {
+      return $entity->access('update');
+    }
   }
 
   /**
@@ -65,13 +71,16 @@ protected function validateAndUpcastRequestAttributes(Request $request) {
 
     // Validate the field name and language.
     $field_name = $request->attributes->get('field_name');
-    if (!$field_name || !field_info_instance($entity->entityType(), $field_name, $entity->bundle())) {
+    if (!$field_name) {
       throw new NotFoundHttpException();
     }
     $langcode = $request->attributes->get('langcode');
     if (!$langcode || (field_valid_language($langcode) !== $langcode)) {
       throw new NotFoundHttpException();
     }
+    if (!($field_definition = $entity->getTranslation($langcode)->get($field_name)->getFieldDefinition())) {
+      throw new NotFoundHttpException();
+    }
   }
 
 }
diff --git a/core/modules/edit/lib/Drupal/edit/EditController.php b/core/modules/edit/lib/Drupal/edit/EditController.php
index 2979e89..a3ccb9f 100644
--- a/core/modules/edit/lib/Drupal/edit/EditController.php
+++ b/core/modules/edit/lib/Drupal/edit/EditController.php
@@ -25,6 +25,7 @@
 use Drupal\edit\Ajax\EntitySavedCommand;
 use Drupal\edit\Ajax\MetadataCommand;
 use Drupal\user\TempStoreFactory;
+use Drupal\field\FieldInstanceInterface;
 
 /**
  * Returns responses for Edit module routes.
@@ -132,12 +133,15 @@ public function metadata(Request $request) {
       }
 
       // Validate the field name and language.
-      if (!$field_name || !($instance = $this->fieldInfo->getInstance($entity->entityType(), $entity->bundle(), $field_name))) {
+      if (!$field_name) {
         throw new NotFoundHttpException();
       }
       if (!$langcode || (field_valid_language($langcode) !== $langcode)) {
         throw new NotFoundHttpException();
       }
+      if (!($field_definition = $entity->getTranslation($langcode)->get($field_name)->getFieldDefinition())) {
+        throw new NotFoundHttpException();
+      }
 
       // If the entity information for this field is requested, include it.
       $entity_id = $entity->entityType() . '/' . $entity_id;
@@ -145,7 +149,7 @@ public function metadata(Request $request) {
         $metadata[$entity_id] = $this->metadataGenerator->generateEntity($entity, $langcode);
       }
 
-      $metadata[$field] = $this->metadataGenerator->generateField($entity, $instance, $langcode, $view_mode);
+      $metadata[$field] = $this->metadataGenerator->generateField($entity, $field_definition, $langcode, $view_mode);
     }
 
     return new JsonResponse($metadata);
@@ -216,7 +220,14 @@ public function fieldForm(EntityInterface $entity, $field_name, $langcode, $view
       $entity = $this->tempStoreFactory->get('edit')->get($entity->uuid());
       // @todo Remove when http://drupal.org/node/1346214 is complete.
       $entity = $entity->getBCEntity();
-      $output = field_view_field($entity, $field_name, $view_mode_id, $langcode);
+
+      $field = $entity->getNGEntity()->get($field_name);
+      if ($field->getFieldDefinition() instanceof FieldInstanceInterface) {
+        $output = field_view_field($entity, $field_name, $view_mode_id, $langcode);
+      }
+      else {
+        $output = \Drupal::service('plugin.manager.field.formatter')->viewBaseField($field);
+      }
 
       $response->addCommand(new FieldFormSavedCommand(drupal_render($output)));
     }
diff --git a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php
index e14e0d2..05312d8 100644
--- a/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php
+++ b/core/modules/edit/lib/Drupal/edit/Form/EditFieldForm.php
@@ -10,6 +10,7 @@
 use Drupal;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\user\TempStoreFactory;
+use Drupal\field\FieldInstanceInterface;
 
 /**
  * Builds and process a form for editing a single entity field.
@@ -33,7 +34,13 @@ public function build(array $form, array &$form_state, EntityInterface $entity,
     $this->tempStoreFactory = $temp_store_factory;
 
     // Add the field form.
-    field_attach_form($form_state['entity'], $form, $form_state, $form_state['langcode'], array('field_name' =>  $form_state['field_name']));
+    $field = $entity->getNGEntity()->get($field_name);
+    if ($field->getFieldDefinition() instanceof FieldInstanceInterface) {
+      field_attach_form($form_state['entity'], $form, $form_state, $form_state['langcode'], array('field_name' =>  $form_state['field_name']));
+    }
+    else {
+      $form[$field_name] = \Drupal::service('plugin.manager.field.widget')->baseFieldForm($field, $form, $form_state, $form_state['langcode']);
+    }
 
     // Add a submit button. Give it a class for easy JavaScript targeting.
     $form['actions'] = array('#type' => 'actions');
@@ -90,7 +97,14 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name
    */
   public function validate(array $form, array &$form_state) {
     $entity = $this->buildEntity($form, $form_state);
-    field_attach_form_validate($entity, $form, $form_state, array('field_name' =>  $form_state['field_name']));
+    $field_name = $form_state['field_name'];
+    $field = $entity->getNGEntity()->get($field_name);
+    if ($field->getFieldDefinition() instanceof FieldInstanceInterface) {
+      field_attach_form_validate($entity, $form, $form_state, array('field_name' => $field_name));
+    }
+    else {
+      // @todo
+    }
   }
 
   /**
@@ -112,13 +126,19 @@ public function submit(array $form, array &$form_state) {
   protected function buildEntity(array $form, array &$form_state) {
     $entity = clone $form_state['entity'];
 
-    field_attach_extract_form_values($entity, $form, $form_state, array('field_name' =>  $form_state['field_name']));
+    $field_name = $form_state['field_name'];
+    $field = $entity->getNGEntity()->get($field_name);
+    if ($field->getFieldDefinition() instanceof FieldInstanceInterface) {
+      field_attach_extract_form_values($entity, $form, $form_state, array('field_name' => $field_name));
+    }
+    else {
+      \Drupal::service('plugin.manager.field.widget')->baseFieldExtractFormValues($field, $form, $form_state, $form_state['langcode']);
+    }
 
     // @todo Refine automated log messages and abstract them to all entity
     //   types: http://drupal.org/node/1678002.
     if ($entity->entityType() == 'node' && $entity->isNewRevision() && !isset($entity->log)) {
-      $instance = field_info_instance($entity->entityType(), $form_state['field_name'], $entity->bundle());
-      $entity->log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $instance['label']));
+      $entity->log = t('Updated the %field-name field through in-place editing.', array('%field-name' => $field->getFieldDefinition()->getFieldLabel()));
     }
 
     return $entity;
diff --git a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
index 74d13f9..279019e 100644
--- a/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
+++ b/core/modules/edit/lib/Drupal/edit/MetadataGenerator.php
@@ -77,7 +77,13 @@ public function generateField(EntityInterface $entity, FieldDefinitionInterface
     }
 
     // Early-return if no editor is available.
-    $formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($field_name)->getPluginId();
+    if ($field_definition instanceof FieldInstanceInterface) {
+      $formatter_id = entity_get_render_display($entity, $view_mode)->getRenderer($field_name)->getPluginId();
+    }
+    else {
+      $field_type_info = field_info_field_types($field_definition->getFieldType());
+      $formatter_id = $field_type_info['default_formatter'];
+    }
     $items = $entity->getTranslation($langcode)->get($field_name)->getValue();
     $editor_id = $this->editorSelector->getEditor($formatter_id, $field_definition, $items);
     if (!isset($editor_id)) {
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index c355e67..db840f5 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -177,6 +177,9 @@ function field_theme() {
     'field' => array(
       'render element' => 'element',
     ),
+    'field__title' => array(
+      'base hook' => 'field',
+    ),
     'field_multiple_value_form' => array(
       'render element' => 'element',
     ),
@@ -1004,6 +1007,13 @@ function theme_field($variables) {
 }
 
 /**
+ * @todo Document.
+ */
+function theme_field__title($variables) {
+  return '<span' . $variables['attributes'] . '>' . drupal_render($variables['items']) . '</span>';
+}
+
+/**
  * Assembles a partial entity structure with initial IDs.
  *
  * @param stdClass $ids
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php
index 1a58ff4..75c3cdf 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/FieldType/ConfigField.php
@@ -24,6 +24,13 @@ class ConfigField extends Field implements ConfigFieldInterface {
   protected $instance;
 
   /**
+   * @todo Document.
+   *
+   * @var array
+   */
+  protected $instances;
+
+  /**
    * {@inheritdoc}
    */
   public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) {
@@ -37,9 +44,14 @@ public function __construct(array $definition, $name = NULL, TypedDataInterface
    * {@inheritdoc}
    */
   public function getInstance() {
-    if (!isset($this->instance) && $parent = $this->getParent()) {
-      $instances = FieldAPI::fieldInfo()->getBundleInstances($parent->entityType(), $parent->bundle());
-      $this->instance = $instances[$this->getName()];
+    if (!isset($this->instance)) {
+      if (!isset($this->instances) && $parent = $this->getParent()) {
+        $this->instances = FieldAPI::fieldInfo()->getBundleInstances($parent->entityType(), $parent->bundle());
+      }
+      $field_name = $this->getName();
+      if (isset($this->instances[$field_name])) {
+        $this->instance = $this->instances[$field_name];
+      }
     }
     return $this->instance;
   }
@@ -48,7 +60,7 @@ public function getInstance() {
    * {@inheritdoc}
    */
   public function getFieldDefinition() {
-    return $this->getInstance();
+    return $this->getInstance() ?: parent::getFieldDefinition();
   }
 
   /**
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php
index aa93124..f2d9a61 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php
@@ -135,7 +135,7 @@ protected function checkFieldAccess($op, $entity) {
       return field_access($op, $field, $entity->entityType(), $entity);
     }
     else {
-      return FALSE;
+      return TRUE;
     }
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
index c916a9f..34334be 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php
@@ -17,7 +17,7 @@
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Core\Plugin\Discovery\AlterDecorator;
-use Drupal\field\Entity\FieldInstance;
+use Drupal\Core\Entity\Field\FieldInterface;
 
 /**
  * Plugin type manager for field formatters.
@@ -207,4 +207,28 @@ public function getDefaultSettings($type) {
     return isset($info['settings']) ? $info['settings'] : array();
   }
 
+  /**
+   * @todo Document.
+   */
+  public function viewBaseField(FieldInterface $field) {
+    $options = array(
+      'field_definition' => $field->getFieldDefinition(),
+      'view_mode' => 'default',
+      'configuration' => array(
+        'label' => 'hidden',
+      ),
+    );
+    $formatter = $this->getInstance($options);
+
+    $entity = $field->getParent()->getBCEntity();
+    $entity_id = $entity->id();
+    $langcode = $entity->language()->id;
+
+    $items_multi = array($entity_id => $field);
+    $formatter->prepareView(array($entity_id => $entity), $langcode, $items_multi);
+    $result = $formatter->view($entity, $langcode, $field);
+    $field_name = $field->getName();
+    return isset($result[$field_name]) ? $result[$field_name] : array();
+  }
+
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
index 7c7a343..5fc83ff 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php
@@ -444,7 +444,7 @@ protected function checkFieldAccess($op, $entity) {
       return field_access($op, $field, $entity->entityType(), $entity);
     }
     else {
-      return FALSE;
+      return TRUE;
     }
   }
 
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
index 12bb296..8816a5e 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Plugin\Discovery\CacheDecorator;
 use Drupal\Core\Plugin\Discovery\AlterDecorator;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Entity\Field\FieldInterface;
 
 /**
  * Plugin type manager for field widgets.
@@ -206,4 +207,45 @@ public function getDefaultSettings($type) {
     return isset($info['settings']) ? $info['settings'] : array();
   }
 
+  /**
+   * @todo Document.
+   */
+  public function baseFieldForm(FieldInterface $field, array &$form, array &$form_state, $langcode) {
+    $options = array(
+      'field_definition' => $field->getFieldDefinition(),
+      'form_mode' => 'default',
+      'configuration' => array(),
+    );
+    if (($field_data_definition = $field->getDefinition()) && isset($field_data_definition['default_widget'])) {
+      $options['configuration']['type'] = $field_data_definition['default_widget'];
+    }
+    $widget = $this->getInstance($options);
+
+    $entity = $field->getParent()->getBCEntity();
+
+    $form += array('#parents' => array());
+    $result = $widget->form($entity, $langcode, $field, $form, $form_state);
+    $field_name = $field->getName();
+    return isset($result[$field_name]) ? $result[$field_name] : array();
+  }
+
+  /**
+   * @todo Document.
+   */
+  public function baseFieldExtractFormValues(FieldInterface $field, array &$form, array &$form_state, $langcode) {
+    $options = array(
+      'field_definition' => $field->getFieldDefinition(),
+      'form_mode' => 'default',
+      'configuration' => array(),
+    );
+    if (($field_data_definition = $field->getDefinition()) && isset($field_data_definition['default_widget'])) {
+      $options['configuration']['type'] = $field_data_definition['default_widget'];
+    }
+    $widget = $this->getInstance($options);
+
+    $entity = $field->getParent()->getBCEntity();
+
+    $widget->extractFormValues($entity, $langcode, $field, $form, $form_state);
+  }
+
 }
diff --git a/core/modules/node/lib/Drupal/node/NodeFormController.php b/core/modules/node/lib/Drupal/node/NodeFormController.php
index 08f54d1..52f677d 100644
--- a/core/modules/node/lib/Drupal/node/NodeFormController.php
+++ b/core/modules/node/lib/Drupal/node/NodeFormController.php
@@ -101,14 +101,8 @@ public function form(array $form, array &$form_state) {
 
     $node_type = node_type_load($node->getType());
     if ($node_type->has_title) {
-      $form['title'] = array(
-        '#type' => 'textfield',
-        '#title' => check_plain($node_type->title_label),
-        '#required' => TRUE,
-        '#default_value' => $node->title,
-        '#maxlength' => 255,
-        '#weight' => -5,
-      );
+      $form['title'] = \Drupal::service('plugin.manager.field.widget')->baseFieldForm($node->getNGEntity()->title, $form, $form_state, $this->getFormLangcode($form_state));
+      $form['title']['#weight'] = -5;
     }
 
     $language_configuration = module_invoke('language', 'get_default_configuration', 'node', $node->getType());
@@ -322,6 +316,15 @@ protected function actions(array $form, array &$form_state) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function buildEntity(array $form, array &$form_state) {
+    $node = parent::buildEntity($form, $form_state);
+    \Drupal::service('plugin.manager.field.widget')->baseFieldExtractFormValues($node->getNGEntity()->title, $form, $form_state, $this->getFormLangcode($form_state));
+    return $node;
+  }
+
+  /**
    * Overrides Drupal\Core\Entity\EntityFormController::validate().
    */
   public function validate(array $form, array &$form_state) {
diff --git a/core/modules/node/lib/Drupal/node/NodeStorageController.php b/core/modules/node/lib/Drupal/node/NodeStorageController.php
index 1a874fe..aa5b9a3 100644
--- a/core/modules/node/lib/Drupal/node/NodeStorageController.php
+++ b/core/modules/node/lib/Drupal/node/NodeStorageController.php
@@ -154,14 +154,12 @@ public function baseFieldDefinitions() {
     $properties['title'] = array(
       'label' => t('Title'),
       'description' => t('The title of this node, always treated as non-markup plain text.'),
-      'type' => 'string_field',
+      'type' => 'field_item:text',
       'required' => TRUE,
       'settings' => array(
         'default_value' => '',
       ),
-      'property_constraints' => array(
-        'value' => array('Length' => array('max' => 255)),
-      ),
+      'default_widget' => 'node_title',
     );
     $properties['uid'] = array(
       'label' => t('User ID'),
diff --git a/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php b/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php
new file mode 100644
index 0000000..b1c64a5
--- /dev/null
+++ b/core/modules/node/lib/Drupal/node/Plugin/field/widget/TitleWidget.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Plugin\field\widget\TitleWidget.
+ */
+
+namespace Drupal\node\Plugin\field\widget;
+
+use Drupal\field\Annotation\FieldWidget;
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\Field\FieldInterface;
+use Drupal\field\Plugin\Type\Widget\WidgetBase;
+
+/**
+ * Plugin implementation of the 'node_title' widget.
+ *
+ * @FieldWidget(
+ *   id = "node_title",
+ *   module = "node",
+ *   label = @Translation("Node title field"),
+ *   field_types = {
+ *     "text"
+ *   }
+ * )
+ */
+class TitleWidget extends WidgetBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function form(EntityInterface $entity, $langcode, FieldInterface $items, array &$form, array &$form_state, $get_delta = NULL) {
+    $field_name = $this->fieldDefinition->getFieldName();
+
+    // @todo Make EntityManager::getFieldDefinitions() allow for per-bundle
+    //   definitions of base fields, so that here, we could just call
+    //   $this->fieldDefinition->getFieldLabel() instead.
+    if ($entity->entityType() == 'node' && $field_name == 'title') {
+      $node_type = node_type_load($entity->bundle());
+      $label = $node_type->title_label;
+    }
+    else {
+      $label = $this->fieldDefinition->getFieldLabel();
+    }
+
+    $addition[$field_name] = array(
+      '#type' => 'textfield',
+      '#title' => check_plain($label),
+      '#required' => $items->isRequired(),
+      '#default_value' => isset($items[0]->value) ? $items[0]->value : '',
+      '#maxlength' => $this->fieldDefinition->getFieldSetting('max_length'),
+    );
+    return $addition;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extractFormValues(EntityInterface $entity, $langcode, FieldInterface $items, array $form, array &$form_state) {
+    $field_name = $this->fieldDefinition->getFieldName();
+
+    // Extract the values from $form_state['values'].
+    $path = array_merge($form['#parents'], array($field_name));
+    $key_exists = NULL;
+    $value = NestedArray::getValue($form_state['values'], $path, $key_exists);
+
+    if ($key_exists) {
+      $items->setValue(array(array('value' => $value)));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldInterface $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
+    return array();
+  }
+}
diff --git a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
index 31d515f..6ee7d48 100644
--- a/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/Condition/NodeConditionTest.php
@@ -14,7 +14,7 @@
  */
 class NodeConditionTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'node', 'field');
+  public static $modules = array('system', 'node', 'field', 'text');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php
index c23332e..ed3ae9c 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTitleTest.php
@@ -59,7 +59,7 @@ function testNodeTitle() {
     $this->assertEqual(current($this->xpath($xpath)), $node->label(), 'Node breadcrumb is equal to node title.', 'Node');
 
     // Test node title in comment preview.
-    $this->assertEqual(current($this->xpath('//article[@id=:id]/h2/a', array(':id' => 'node-' . $node->id()))), $node->label(), 'Node preview title is equal to node title.', 'Node');
+    $this->assertEqual(current($this->xpath('//article[@id=:id]/h2/a/span', array(':id' => 'node-' . $node->id()))), $node->label(), 'Node preview title is equal to node title.', 'Node');
 
     // Test node title is clickable on teaser list (/node).
     $this->drupalGet('node');
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
index dcba3e2..09c522f 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeValidationTest.php
@@ -55,13 +55,13 @@ public function testValidation() {
     $violations = $node->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when title is too long.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'title.0.value');
-    $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', array('%limit' => 255)));
+    $this->assertEqual($violations[0]->getMessage(), '<em class="placeholder">Title</em>: the text may not be longer than 255 characters.');
 
     $node->set('title', NULL);
     $violations = $node->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when title is not set.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'title');
-    $this->assertEqual($violations[0]->getMessage(), t('This value should not be null.'));
+    $this->assertEqual($violations[0]->getMessage(), 'This value should not be null.');
 
     // Make the title valid again.
     $node->set('title', $this->randomString());
@@ -72,6 +72,6 @@ public function testValidation() {
     $violations = $node->validate();
     $this->assertEqual(count($violations), 1, 'Violation found when changed date is before the last changed date.');
     $this->assertEqual($violations[0]->getPropertyPath(), 'changed.0.value');
-    $this->assertEqual($violations[0]->getMessage(), t('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'));
+    $this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
   }
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 2353be3..a7de2b8 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -700,7 +700,7 @@ function template_preprocess_node(&$variables) {
 
   $uri = $node->uri();
   $variables['node_url']  = url($uri['path'], $uri['options']);
-  $variables['label'] = check_plain($node->label());
+  $variables['label'] = Drupal::service('plugin.manager.field.formatter')->viewBaseField($node->getNGEntity()->title);
   $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
 
   // Helpful $content variable for templates.
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
index 9e1ecb8..b41579a 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php
@@ -2099,7 +2099,7 @@ protected function assertNoLinkByHref($href, $message = '', $group = 'Other') {
    */
   protected function clickLink($label, $index = 0) {
     $url_before = $this->getUrl();
-    $urls = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
+    $urls = $this->xpath('//a[normalize-space()=:label]', array(':label' => $label));
 
     if (isset($urls[$index])) {
       $url_target = $this->getAbsoluteUrl($urls[$index]['href']);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
index b04e2d8..cb8ec55 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/ContextPluginTest.php
@@ -17,7 +17,7 @@
  */
 class ContextPluginTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'user', 'node');
+  public static $modules = array('system', 'user', 'node', 'text');
 
   public static function getInfo() {
     return array(
