diff --git a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
index bc6184c..df3c6a0 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/views/display/Block.php
@@ -77,7 +77,8 @@ public function executeHookBlockList($delta = 0, $edit = array()) {
   public function execute() {
     // Prior to this being called, the $view should already be set to this
     // display, and arguments should be set on the view.
-    $info['content'] = $this->view->render();
+    $element = $this->view->render();
+    $info['content'] = drupal_render($element);
     $info['subject'] = filter_xss_admin($this->view->getTitle());
     if (!empty($this->view->result) || $this->getOption('empty') || !empty($this->view->style_plugin->definition['even empty'])) {
       return $info;
diff --git a/core/modules/search/lib/Drupal/search/Plugin/views/row/SearchRow.php b/core/modules/search/lib/Drupal/search/Plugin/views/row/SearchRow.php
index 9bebc88..3b73726 100644
--- a/core/modules/search/lib/Drupal/search/Plugin/views/row/SearchRow.php
+++ b/core/modules/search/lib/Drupal/search/Plugin/views/row/SearchRow.php
@@ -43,12 +43,12 @@ public function buildOptionsForm(&$form, &$form_state) {
    * Override the behavior of the render() function.
    */
   function render($row) {
-    return theme($this->themeFunctions(),
-      array(
-        'view' => $this->view,
-        'options' => $this->options,
-        'row' => $row
-      ));
+    return array(
+      '#theme' => $this->themeFunctions(),
+      '#view' => $this->view,
+      '#options' => $this->options,
+      '#row' => $row,
+    );
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
index 62b725c..886ffa4 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/views/row/EntityRow.php
@@ -135,6 +135,6 @@ public function pre_render($result) {
    */
   function render($row) {
     $entity_id = $row->{$this->field_alias};
-    return drupal_render($this->build[$entity_id]);
+    return $this->build[$entity_id];
   }
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php
index fb747ea..63d1ab9 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/area/AreaPluginBase.php
@@ -149,6 +149,15 @@ public function tokenForm(&$form, &$form_state) {
   public function query() { }
 
   /**
+   * Performs any operations needed before full rendering.
+   *
+   * @param array $results
+   *  The results of the view.
+   */
+  public function preRender(array $results) {
+  }
+
+  /**
    * Render the area
    */
   function render($empty = FALSE) {
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
index 618de3c..850f7c2 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/cache/CachePluginBase.php
@@ -247,12 +247,20 @@ function gather_headers() {
     }
 
     // Slightly less simple for CSS:
-    $css = drupal_add_css();
+    // Load the css and the js from both the global css and the ones attached
+    // to the view directly.
+    if (!isset($this->view->element['#attached']['css'])) {
+      $this->view->element['#attached']['css'] = array();
+    }
+    $css = drupal_array_merge_deep(drupal_add_css(), $this->view->element['#attached']['css']);
     $css_start = isset($this->storage['css']) ? $this->storage['css'] : array();
     $this->storage['css'] = array_diff_assoc($css, $css_start);
 
     // Get javascript after/before views renders.
-    $js = drupal_add_js();
+    if (!isset($this->view->element['#attached']['js'])) {
+      $this->view->element['#attached']['js'] = array();
+    }
+    $js = drupal_array_merge_deep(drupal_add_js(), $this->view->element['#attached']['js']);
     $js_start = isset($this->storage['js']) ? $this->storage['js'] : array();
     // If there are any differences between the old and the new javascript then
     // store them to be added later.
@@ -273,17 +281,17 @@ function restore_headers() {
     }
     if (!empty($this->storage['css'])) {
       foreach ($this->storage['css'] as $args) {
-        drupal_add_css($args['data'], $args);
+        $this->view->element['#attached']['css'][] = $args;
       }
     }
     if (!empty($this->storage['js'])) {
       foreach ($this->storage['js'] as $key => $args) {
         if ($key !== 'settings') {
-          drupal_add_js($args['data'], $args);
+          $this->view->element['#attached']['js'][] = $args;
         }
         else {
           foreach ($args as $setting) {
-            drupal_add_js($setting, 'setting');
+            $this->view->element['#attached']['js']['setting'][] = $setting;
           }
         }
       }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index eabea9d..495edd9 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -926,7 +926,6 @@ public function overrideOption($option, $value) {
    * an easy URL to exactly the right section. Don't override this.
    */
   public function optionLink($text, $section, $class = '', $title = '') {
-    views_add_js('ajax');
     if (!empty($class)) {
       $text = '<span>' . $text . '</span>';
     }
@@ -2469,7 +2468,11 @@ public function hookMenu() { return array(); }
    * Render this display.
    */
   public function render() {
-    return theme($this->themeFunctions(), array('view' => $this->view));
+    return array(
+      '#theme' => $this->themeFunctions(),
+      '#view' => $this->view,
+      '#attached' => $this->view->element['#attached'],
+    );
   }
 
   public function renderArea($area, $empty = FALSE) {
@@ -2541,7 +2544,10 @@ public function execute() { }
    * Fully render the display for the purposes of a live preview or
    * some other AJAXy reason.
    */
-  function preview() { return $this->view->render(); }
+  function preview() {
+    $element = $this->view->render();
+    return drupal_render($element);
+  }
 
   /**
    * Displays can require a certain type of style plugin. By default, they will
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
index 97a6656..1fa16ac 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/row/RowPluginBase.php
@@ -159,13 +159,13 @@ function pre_render($result) { }
    *   The rendered output of a single row, used by the style plugin.
    */
   function render($row) {
-    return theme($this->themeFunctions(),
-      array(
-        'view' => $this->view,
-        'options' => $this->options,
-        'row' => $row,
-        'field_alias' => isset($this->field_alias) ? $this->field_alias : '',
-      ));
+    return array(
+      '#theme' => $this->themeFunctions(),
+      '#view' => $this->view,
+      '#options' => $this->options,
+      '#row' => $row,
+      '#field_alias' => isset($this->field_alias) ? $this->field_alias : '',
+    );
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultSummary.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultSummary.php
index 5cb448f..d3db599 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultSummary.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/DefaultSummary.php
@@ -84,11 +84,12 @@ function render() {
       $rows[] = $row;
     }
 
-    return theme($this->themeFunctions(), array(
-      'view' => $this->view,
-      'options' => $this->options,
-      'rows' => $rows
-    ));
+    return array(
+      '#theme' => $this->themeFunctions(),
+      '#view' => $this->view,
+      '#options' => $this->options,
+      '#rows' => $rows,
+    );
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/Mapping.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/Mapping.php
index 8e9649e..3914fd8 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/Mapping.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/Mapping.php
@@ -132,12 +132,13 @@ public function buildOptionsForm(&$form, &$form_state) {
    * Provides the mapping definition as an available variable.
    */
   function render() {
-    return theme($this->themeFunctions(), array(
-      'view' => $this->view,
-      'options' => $this->options,
-      'rows' => $this->view->result,
-      'mapping' => $this->defineMapping(),
-    ));
+    return array(
+      '#theme' => $this->themeFunctions(),
+      '#view' => $this->view,
+      '#options' => $this->options,
+      '#rows' => $this->view->result,
+      '#mapping' => $this->defineMapping(),
+    );
   }
 
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
index ee8081d..abed751 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/style/StylePluginBase.php
@@ -431,18 +431,19 @@ function render() {
    *   Rendered output of given grouping sets.
    */
   function render_grouping_sets($sets, $level = 0) {
-    $output = '';
+    $output = array();
+    $theme_functions = views_theme_functions('views_view_grouping', $this->view, $this->view->display_handler->display);
     foreach ($sets as $set) {
       $row = reset($set['rows']);
       // Render as a grouping set.
       if (is_array($row) && isset($row['group'])) {
-        $output .= theme(views_theme_functions('views_view_grouping', $this->view, $this->view->display_handler->display),
-          array(
-            'view' => $this->view,
-            'grouping' => $this->options['grouping'][$level],
-            'grouping_level' => $level,
-            'rows' => $set['rows'],
-            'title' => $set['group'])
+        $output[] = array(
+          '#theme' => $theme_functions,
+          '#view' => $this->view,
+          '#grouping' => $this->options['grouping'][$level],
+          '#grouping_level' => $level,
+          '#rows' => $set['rows'],
+          '#title' => $set['group'],
         );
       }
       // Render as a record set.
@@ -450,17 +451,19 @@ function render_grouping_sets($sets, $level = 0) {
         if ($this->usesRowPlugin()) {
           foreach ($set['rows'] as $index => $row) {
             $this->view->row_index = $index;
-            $set['rows'][$index] = $this->row_plugin->render($row);
+            $render = $this->row_plugin->render($row);
+            // Row render arrays cannot be contained by style render arrays.
+            $set['rows'][$index] = drupal_render($render);
           }
         }
 
-        $output .= theme($this->themeFunctions(),
-          array(
-            'view' => $this->view,
-            'options' => $this->options,
-            'grouping_level' => $level,
-            'rows' => $set['rows'],
-            'title' => $set['group'])
+        $output[] = array(
+          '#theme' => $this->themeFunctions(),
+          '#view' => $this->view,
+          '#options' => $this->options,
+          '#grouping_level' => $level,
+          '#rows' => $set['rows'],
+          '#title' => $set['group'],
         );
       }
     }
diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php
index 6aa64c5..1e9e675 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/PagerTest.php
@@ -260,6 +260,7 @@ public function testRenderNullPager() {
     $view->use_ajax = TRUE; // force the value again here
     $view->pager = NULL;
     $output = $view->render();
+    $output = drupal_render($output);
     $this->assertEqual(preg_match('/<ul class="pager">/', $output), 0, t('The pager is not rendered.'));
   }
 
diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php
index 0367905..414d659 100644
--- a/core/modules/views/lib/Drupal/views/ViewExecutable.php
+++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php
@@ -398,6 +398,20 @@ class ViewExecutable {
   public $dom_id;
 
   /**
+   * A render array container to store render related information.
+   *
+   * For example you can alter the array and attach some css/js via the
+   * #attached key. This is the required way to add custom css/js.
+   *
+   * @var array
+   *
+   * @see drupal_process_attached
+   */
+  public $element = array(
+    '#attached' => array(),
+  );
+
+  /**
    * Constructs a new ViewExecutable object.
    *
    * @param Drupal\views\Plugin\Core\Entity\View $storage
@@ -407,6 +421,9 @@ public function __construct(View $storage) {
     // Reference the storage and the executable to each other.
     $this->storage = $storage;
     $this->storage->setExecutable($this);
+
+    // Add the default css for a view.
+    $this->element['#attached']['css'][] = drupal_get_path('module', 'views') . '/css/views.base.css';
   }
 
   /**
@@ -1286,6 +1303,13 @@ public function render($display_id = NULL) {
 
       $this->style_plugin->pre_render($this->result);
 
+      // Let each area handler have access to the result set.
+      foreach (array('header', 'footer', 'empty') as $area) {
+        foreach ($this->{$area} as $handler) {
+          $handler->preRender($this->result);
+        }
+      }
+
       // Let modules modify the view just prior to rendering it.
       foreach (module_implements('views_pre_render') as $module) {
         $function = $module . '_views_pre_render';
@@ -2143,6 +2167,15 @@ public function getItem($display_id, $type, $id) {
   }
 
   /**
+   * Sets the build array used by the view.
+   *
+   * @param array $element
+   */
+  public function setElement(&$element) {
+    $this->element =& $element;
+  }
+
+  /**
    * Sets the configuration of a handler instance on a given display.
    *
    * @param string $display_id
diff --git a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php
index 1cc7ef5..cec3a1d 100644
--- a/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php
+++ b/core/modules/views/tests/views_test_data/lib/Drupal/views_test_data/Plugin/views/display/DisplayTest.php
@@ -114,10 +114,9 @@ public function submitOptionsForm(&$form, &$form_state) {
   public function execute() {
     $this->view->build();
 
+    $render = $this->view->render();
     // Render the test option as the title before the view output.
-    $render = '<h1>' . filter_xss_admin($this->options['test_option']) . '</h1>';
-    // And now render the view.
-    $render .= $this->view->render();
+    $render['#prefix'] = '<h1>' . filter_xss_admin($this->options['test_option']) . '</h1>';
 
     return $render;
   }
@@ -128,7 +127,8 @@ public function execute() {
    * Override so preview and execute are the same output.
    */
   public function preview() {
-    return $this->execute();
+    $element = $this->execute();
+    return drupal_render($element);
   }
 
 }
diff --git a/core/modules/views/tests/views_test_data/views_test_data.module b/core/modules/views/tests/views_test_data/views_test_data.module
index bf813e8..2de6688 100644
--- a/core/modules/views/tests/views_test_data/views_test_data.module
+++ b/core/modules/views/tests/views_test_data/views_test_data.module
@@ -78,6 +78,9 @@ function views_test_data_views_pre_render(ViewExecutable $view) {
   if ($view->storage->get('name') == 'test_cache_header_storage') {
     drupal_add_js(drupal_get_path('module', 'views_test_data') . '/views_cache.test.js');
     drupal_add_css(drupal_get_path('module', 'views_test_data') . '/views_cache.test.css');
+    $path = drupal_get_path('module', 'views_test_data');
+    $view->element['js'][] = "$path/views_cache.test.js";
+    $view->element['css'][] = "$path/views_cache.test.css";
     $view->build_info['pre_render_called'] = TRUE;
   }
 }
diff --git a/core/modules/views/theme/theme.inc b/core/modules/views/theme/theme.inc
index 0a45642..81cd0e6 100644
--- a/core/modules/views/theme/theme.inc
+++ b/core/modules/views/theme/theme.inc
@@ -47,7 +47,11 @@ function template_preprocess_views_view(&$vars) {
 
   $view = $vars['view'];
 
-  $vars['rows']       = (!empty($view->result) || $view->style_plugin->even_empty()) ? $view->style_plugin->render($view->result) : '';
+  $vars['rows'] = (!empty($view->result) || $view->style_plugin->even_empty()) ? $view->style_plugin->render($view->result) : '';
+  // Force a render array so CSS/JS can be added.
+  if (!is_array($vars['rows'])) {
+    $vars['rows'] = array('#markup' => $vars['rows']);
+  }
 
   $vars['css_name']   = drupal_clean_css_identifier($view->storage->get('name'));
   $vars['name']       = $view->storage->get('name');
@@ -68,13 +72,14 @@ function template_preprocess_views_view(&$vars) {
     $vars['attributes']['class'][] = $vars['css_class'];
   }
 
-  $empty = empty($vars['rows']);
+  // Render the rows render array to check for contents.
+  $rows = $vars['rows'];
+  $rows = drupal_render($rows);
+  $empty = empty($rows);
 
   $vars['header'] = $view->display_handler->renderArea('header', $empty);
   $vars['footer'] = $view->display_handler->renderArea('footer', $empty);
-  if ($empty) {
-    $vars['empty'] = $view->display_handler->renderArea('empty', $empty);
-  }
+  $vars['empty'] = $empty ? $view->display_handler->renderArea('empty', $empty) : FALSE;
 
   $vars['exposed']    = !empty($view->exposed_widgets) ? $view->exposed_widgets : '';
   $vars['more']       = $view->display_handler->renderMoreLink();
@@ -138,14 +143,13 @@ function template_preprocess_views_view(&$vars) {
         ),
       ),
     );
-
-    drupal_add_js($settings, 'setting');
-    views_add_js('ajax_view');
+    $view->element['#attached']['js'][] = array('type' => 'setting', 'data' => $settings);
+    $view->element['#attached']['library'][] = array('views', 'views.ajax');
   }
 
   // If form fields were found in the View, reformat the View output as a form.
   if (views_view_has_form_elements($view)) {
-    $output = !empty($vars['rows']) ? $vars['rows'] : $vars['empty'];
+    $output = !empty($rows) ? $rows : $vars['empty'];
     $form = drupal_get_form(views_form_id($view), $view, $output);
     // The form is requesting that all non-essential views elements be hidden,
     // usually because the rendered step is not a view result.
@@ -162,15 +166,6 @@ function template_preprocess_views_view(&$vars) {
 }
 
 /**
- * Process function to render certain elements into the view.
- */
-function template_process_views_view(&$vars) {
-  if (is_array($vars['rows'])) {
-    $vars['rows'] = drupal_render($vars['rows']);
-  }
-}
-
-/**
  * Preprocess theme function to print a single record from a row, with fields
  */
 function template_preprocess_views_view_fields(&$vars) {
@@ -652,7 +647,7 @@ function template_preprocess_views_view_table(&$vars) {
   }
 
   if (!empty($options['sticky'])) {
-    drupal_add_js('misc/tableheader.js');
+    $vars['view']->element['#attached']['library'][] = array('system', 'drupal.tableheader');
     $vars['attributes']['class'][] = "sticky-enabled";
   }
   $vars['attributes']['class'][] = 'cols-'. count($vars['header']);
@@ -664,7 +659,7 @@ function template_preprocess_views_view_table(&$vars) {
   // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM
   // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors.
   if (count($vars['header']) && $responsive) {
-    drupal_add_library('system', 'drupal.tableresponsive');
+    $vars['view']->element['#attached']['library'][] = array('system', 'drupal.tableresponsive');
     // Add 'responsive-enabled' class to the table to identify it for JS.
     // This is needed to target tables constructed by this function.
     $vars['attributes']['class'][] = 'responsive-enabled';
@@ -786,7 +781,7 @@ function template_preprocess_views_view_grid(&$vars) {
   // with the classes represented by the constants RESPONSIVE_PRIORITY_MEDIUM
   // and RESPONSIVE_PRIORITY_LOW, add the tableresponsive behaviors.
   if (count($vars['header']) && $responsive) {
-    drupal_add_library('system', 'drupal.tableresponsive');
+    $vars['view']->element['#attached']['library'][] = array('system', 'drupal.tableresponsive');
     // Add 'responsive-enabled' class to the table to identify it for JS.
     // This is needed to target tables constructed by this function.
     $vars['attributes']['class'][] = 'responsive-enabled';
diff --git a/core/modules/views/theme/views-view.tpl.php b/core/modules/views/theme/views-view.tpl.php
index 8e645a6..2bbb507 100644
--- a/core/modules/views/theme/views-view.tpl.php
+++ b/core/modules/views/theme/views-view.tpl.php
@@ -51,7 +51,7 @@
     </div>
   <?php endif; ?>
 
-  <?php if ($rows): ?>
+  <?php if ($rows = render($rows)): ?>
     <div class="view-content">
       <?php print $rows; ?>
     </div>
diff --git a/core/modules/views/views.info b/core/modules/views/views.info
index 2a15bb9..edd7390 100644
--- a/core/modules/views/views.info
+++ b/core/modules/views/views.info
@@ -3,4 +3,3 @@ description = Create customized lists and queries from your database.
 package = Core
 version = VERSION
 core = 8.x
-stylesheets[all][] = css/views.base.css
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index f6568a4..f752376 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -832,7 +832,7 @@ function views_add_contextual_links(&$render_element, $location, ViewExecutable
     // Also do not do anything if the display plugin has not defined any
     // contextual links that are intended to be displayed in the requested
     // location.
-    $plugin = views_get_plugin_definition('display', $view->displayHandlers[$display_id]->display['display_plugin']);
+    $plugin = views_get_plugin_definition('display', $view->storage->display[$display_id]['display_plugin']);
     // If contextual_links_locations are not set, provide a sane default. (To
     // avoid displaying any contextual links at all, a display plugin can still
     // set 'contextual_links_locations' to, e.g., {""}.)
@@ -1103,36 +1103,24 @@ function views_hook_info() {
 }
 
 /**
- * Include views .css files.
+ * Implements hook_library_info().
  */
-function views_add_css($file) {
-  // We set preprocess to FALSE because we are adding the files conditionally,
-  // and we don't want to generate duplicate cache files.
-  // TODO: at some point investigate adding some files unconditionally and
-  // allowing preprocess.
-  drupal_add_css(drupal_get_path('module', 'views') . "/css/$file.css", array('preprocess' => FALSE));
-}
+function views_library_info() {
+  $path = drupal_get_path('module', 'views') . '/js';
+  $libraries['views.ajax'] = array(
+    'title' => 'Views AJAX',
+    'version' => VERSION,
+    'js' => array(
+      "$path/base.js" => array('group' => JS_DEFAULT),
+      "$path/ajax_view.js" => array('group' => JS_DEFAULT),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery.form'),
+      array('system', 'drupal.ajax'),
+    ),
+  );
 
-/**
- * Include views .js files.
- */
-function views_add_js($file) {
-  // If javascript has been disabled by the user, never add js files.
-  if (config('views.settings')->get('no_javascript')) {
-    return;
-  }
-  $path = drupal_get_path('module', 'views');
-  static $base = TRUE, $ajax = TRUE;
-  if ($base) {
-    drupal_add_js($path . "/js/base.js");
-    $base = FALSE;
-  }
-  if ($ajax && in_array($file, array('ajax', 'ajax_view'))) {
-    drupal_add_library('system', 'drupal.ajax');
-    drupal_add_library('system', 'jquery.form');
-    $ajax = FALSE;
-  }
-  drupal_add_js($path . "/js/$file.js");
+  return $libraries;
 }
 
 /**
@@ -1813,7 +1801,7 @@ function views_exposed_form($form, &$form_state) {
 
   // If using AJAX, we need the form plugin.
   if ($view->use_ajax) {
-    drupal_add_library('system', 'jquery.form');
+    $form['#attached']['library'][] = array('system', 'jquery.form');
   }
 
   $exposed_form_plugin = $form_state['exposed_form_plugin'];
diff --git a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
index dda1c01..509e8b1 100644
--- a/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
+++ b/core/modules/views/views_ui/lib/Drupal/views_ui/ViewUI.php
@@ -1277,6 +1277,7 @@ public function buildEditForm($form, &$form_state, $display_id = NULL) {
     $form['#attached']['library'][] = array('system', 'jquery.form');
     $form['#attached']['library'][] = array('system', 'drupal.states');
     $form['#attached']['library'][] = array('system', 'drupal.tabledrag');
+    $form['#attached']['library'][] = array('views_ui', 'views_ui.ajax');
 
     $form['#attached']['css'] = static::getAdminCSS();
 
diff --git a/core/modules/views/views_ui/views_ui.module b/core/modules/views/views_ui/views_ui.module
index d78ac25..871f888 100644
--- a/core/modules/views/views_ui/views_ui.module
+++ b/core/modules/views/views_ui/views_ui.module
@@ -285,6 +285,30 @@ function views_ui_custom_theme() {
 }
 
 /**
+ * Implements hook_library_info().
+ */
+function views_ui_library_info() {
+  $libraries = array();
+
+  if (!config('views.settings')->get('no_javascript')) {
+    $libraries['views_ui.ajax'] = array(
+      'title' => 'Views UI AJAX',
+      'version' => VERSION,
+      'js' => array(
+        drupal_get_path('module', 'views') . '/js/base.js' => array('group' => JS_DEFAULT),
+        drupal_get_path('module', 'views_ui') . '/js/ajax.js' => array('group' => JS_DEFAULT),
+      ),
+      'dependencies' => array(
+        array('system', 'jquery.form'),
+        array('system', 'drupal.ajax'),
+      ),
+    );
+  }
+
+  return $libraries;
+}
+
+/**
  * Page title callback for the Edit View page.
  */
 function views_ui_edit_page_title(ViewUI $view) {
