Index: src/wp-admin/customize.php
===================================================================
--- src/wp-admin/customize.php	(revision 30975)
+++ src/wp-admin/customize.php	(working copy)
@@ -184,7 +184,9 @@
 	<div id="customize-preview" class="wp-full-overlay-main"></div>
 	<?php
 
-	// Render control templates.
+	// Render Panel, Section, and Control templates.
+	$wp_customize->render_panel_templates();
+	$wp_customize->render_section_templates();
 	$wp_customize->render_control_templates();
 
 	/**
Index: src/wp-admin/js/customize-controls.js
===================================================================
--- src/wp-admin/js/customize-controls.js	(revision 30975)
+++ src/wp-admin/js/customize-controls.js	(working copy)
@@ -162,6 +162,9 @@
 			container.params = {};
 			$.extend( container, options || {} );
 			container.container = $( container.params.content );
+			if ( 0 === container.container.length ) {
+				container.container = $( container.getContainer() );
+			}
 
 			container.deferred = {
 				embedded: new $.Deferred()
@@ -348,7 +351,15 @@
 		 * Bring the container into view and then expand this and bring it into view
 		 * @param {Object} [params]
 		 */
-		focus: focus
+		focus: focus,
+		
+		/**
+		 * To override in a subclass, return the container's container element generated from the template.
+		 * @abstract
+		 */
+		getContainer: function () {
+			throw new Error( 'Must override with subclass.' );
+		}
 	});
 
 	/**
@@ -517,6 +528,23 @@
 				section.container.removeClass( 'open' );
 				content.slideUp( args.duration, args.completeCallback );
 			}
+		},
+
+		/**
+		 * Return the section contents, generated from its JS template, if it exists.
+		 *
+		 * @since 4.2.0
+		 */
+		getContainer: function () {
+			var template,
+				section = this;
+
+			if ( 0 !== $( '#tmpl-customize-section-' + section.params.type ).length ) {
+				template = wp.template( 'customize-section-' + section.params.type );
+				if ( template && section.container ) {
+					return template( section.params );
+				}
+			}
 		}
 	});
 
@@ -553,6 +581,7 @@
 
 			if ( ! panel.container.parent().is( parentContainer ) ) {
 				parentContainer.append( panel.container );
+				panel.renderContent();
 			}
 			panel.deferred.embedded.resolve();
 		},
@@ -575,14 +604,13 @@
 				}
 			});
 
-			meta = panel.container.find( '.panel-meta:first' );
-
-			meta.find( '> .accordion-section-title' ).on( 'click keydown', function( event ) {
+			panel.container.on( 'click keydown', '.panel-meta > .accordion-section-title', function( event ) {
 				if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
 					return;
 				}
 				event.preventDefault(); // Keep this AFTER the key filter above
 
+				meta = panel.container.find( '.panel-meta' );
 				if ( meta.hasClass( 'cannot-expand' ) ) {
 					return;
 				}
@@ -704,6 +732,43 @@
 				panelTitle.focus();
 				container.scrollTop( 0 );
 			}
+		},
+
+		/**
+		 * Return the panel container, generated from its JS template, if it exists.
+		 *
+		 * @since 4.2.0
+		 */
+		getContainer: function () {
+/*			var template, // @todo dynamic panel containers
+				section = this;
+
+			if ( 0 !== $( '#tmpl-customize-panel-' + section.params.type ).length ) {
+				template = wp.template( 'customize-panel-' + section.params.type );
+				if ( template && section.container ) {
+					return template( section.params );
+				}
+			}
+*/		},
+
+		/**
+		 * Render the panel from its JS template, if it exists.
+		 *
+		 * The panel's container must already exist in the DOM.
+		 *
+		 * @since 4.2.0
+		 */
+		renderContent: function () {
+			var template,
+				panel = this;
+
+			// Add the content to the container.
+			if ( 0 !== $( '#tmpl-customize-panel-' + panel.params.type + '-content' ).length ) {
+				template = wp.template( 'customize-panel-' + panel.params.type + '-content' );
+				if ( template && panel.container ) {
+					panel.container.find( '.accordion-sub-container' ).append( template( panel.params ) );
+				}
+			}
 		}
 	});
 
Index: src/wp-includes/class-wp-customize-control.php
===================================================================
--- src/wp-includes/class-wp-customize-control.php	(revision 30975)
+++ src/wp-includes/class-wp-customize-control.php	(working copy)
@@ -259,6 +259,10 @@
 	 * @return array Array of parameters passed to the JavaScript.
 	 */
 	public function json() {
+		if ( ! $this->check_capabilities() ) {
+			return;
+		}
+
 		$this->to_json();
 		return $this->json;
 	}
@@ -515,11 +519,11 @@
 	 * @since 4.1.0
 	 */
 	final public function print_template() {
-	        ?>
-	        <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content">
-	                <?php $this->content_template(); ?>
-	        </script>
-	        <?php
+        ?>
+        <script type="text/html" id="tmpl-customize-control-<?php echo $this->type; ?>-content">
+                <?php $this->content_template(); ?>
+        </script>
+        <?php
 	}
 
 	/**
Index: src/wp-includes/class-wp-customize-manager.php
===================================================================
--- src/wp-includes/class-wp-customize-manager.php	(revision 30975)
+++ src/wp-includes/class-wp-customize-manager.php	(working copy)
@@ -54,8 +54,26 @@
 	protected $customized;
 
 	/**
-	 * Controls that may be rendered from JS templates.
+	 * Panel types that may be rendered from JS templates.
 	 *
+	 * @since 4.2.0
+	 * @access protected
+	 * @var array
+	 */
+	protected $registered_panel_types = array();
+
+	/**
+	 * Section types that may be rendered from JS templates.
+	 *
+	 * @since 4.2.0
+	 * @access protected
+	 * @var array
+	 */
+	protected $registered_section_types = array();
+
+	/**
+	 * Control types that may be rendered from JS templates.
+	 *
 	 * @since 4.1.0
 	 * @access protected
 	 * @var array
@@ -759,6 +777,34 @@
 	}
 
 	/**
+	 * Register a customize panel type.
+	 *
+	 * Registered types are eligible to be rendered via JS and created dynamically.
+	 *
+	 * @since 4.2.0
+	 * @access public
+	 *
+	 * @param string $panel Name of a custom panel which is a subclass of
+	 *                        {@see WP_Customize_Panel}.
+	 */
+	public function register_panel_type( $panel ) {
+		$this->registered_panel_types[] = $panel;
+	}
+
+	/**
+	 * Render JS templates for all registered panel types.
+	 *
+	 * @since 4.2.0
+	 * @access public
+	 */
+	public function render_panel_templates() {
+		foreach ( $this->registered_panel_types as $panel_type ) {
+			$panel = new $panel_type( $this, 'temp', array() );
+			$panel->print_template();
+		}
+	}
+
+	/**
 	 * Add a customize section.
 	 *
 	 * @since 3.4.0
@@ -800,6 +846,34 @@
 	}
 
 	/**
+	 * Register a customize section type.
+	 *
+	 * Registered types are eligible to be rendered via JS and created dynamically.
+	 *
+	 * @since 4.2.0
+	 * @access public
+	 *
+	 * @param string $section Name of a custom section which is a subclass of
+	 *                        {@see WP_Customize_Section}.
+	 */
+	public function register_section_type( $section ) {
+		$this->registered_section_types[] = $section;
+	}
+
+	/**
+	 * Render JS templates for all registered section types.
+	 *
+	 * @since 4.2.0
+	 * @access public
+	 */
+	public function render_section_templates() {
+		foreach ( $this->registered_section_types as $section_type ) {
+			$section = new $section_type( $this, 'temp', array() );
+			$section->print_template();
+		}
+	}
+
+	/**
 	 * Add a customize control.
 	 *
 	 * @since 3.4.0
@@ -970,7 +1044,10 @@
 	 */
 	public function register_controls() {
 
-		/* Control Types (custom control classes) */
+		/* Panel, Section, and Control Types */
+		$this->register_panel_type( 'WP_Customize_Panel' );
+		$this->register_section_type( 'WP_Customize_Section' );
+		$this->register_section_type( 'WP_Customize_Sidebar_Section' );
 		$this->register_control_type( 'WP_Customize_Color_Control' );
 		$this->register_control_type( 'WP_Customize_Upload_Control' );
 		$this->register_control_type( 'WP_Customize_Image_Control' );
Index: src/wp-includes/class-wp-customize-panel.php
===================================================================
--- src/wp-includes/class-wp-customize-panel.php	(revision 30975)
+++ src/wp-includes/class-wp-customize-panel.php	(working copy)
@@ -206,7 +206,11 @@
 	 * @return array The array to be exported to the client as JSON.
 	 */
 	public function json() {
-		$array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) );
+		if ( ! $this->check_capabilities() ) {
+			return;
+		}
+
+		$array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'type' ) );
 		$array['content'] = $this->get_content();
 		$array['active'] = $this->active();
 		$array['instanceNumber'] = $this->instance_number;
@@ -302,25 +306,58 @@
 	}
 
 	/**
-	 * Render the sections that have been added to the panel.
+	 * Render the panel UI in a subclass.
 	 *
+	 * Panel contents are now rendered in JS by default, see {@see WP_Customize_Panel::print_template()}.
+	 *
 	 * @since 4.1.0
 	 * @access protected
 	 */
-	protected function render_content() {
+	protected function render_content() {}
+
+	/**
+	 * Render the panel's JS template.
+	 *
+	 * This function is only run for panel types that have been registered with
+	 * {@see WP_Customize_Manager::register_panel_type()}.
+	 *
+	 * In the future, this will also print the template for the panel's container
+	 * and be override-able.
+	 *
+	 * @since 4.2.0
+	 */
+	final public function print_template() {
+        ?>
+        <script type="text/html" id="tmpl-customize-panel-<?php echo $this->type; ?>-content">
+			<?php $this->content_template(); ?>
+        </script>
+        <?php
+	}
+
+	/**
+	 * An Underscore (JS) template for this panel's content (but not its container).
+	 *
+	 * Class variables for this panel class are available in the `data` JS object;
+	 * export custom variables by overriding {@see WP_Customize_Panel::json()}.
+	 *
+	 * @see WP_Customize_Panel::print_template()
+	 *
+	 * @since 4.2.0
+	 */
+	protected function content_template() {
 		?>
-		<li class="panel-meta accordion-section control-section<?php if ( empty( $this->description ) ) { echo ' cannot-expand'; } ?>">
+		<li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
 			<div class="accordion-section-title" tabindex="0">
 				<span class="preview-notice"><?php
 					/* translators: %s is the site/panel title in the Customizer */
-					echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">' . esc_html( $this->title ) . '</strong>' );
+					echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">{{ data.title }}</strong>' );
 				?></span>
 			</div>
-			<?php if ( ! empty( $this->description ) ) : ?>
+			<# if ( data.description ) { #>
 				<div class="accordion-section-content description">
-					<?php echo $this->description; ?>
+					{{{ data.description }}}
 				</div>
-			<?php endif; ?>
+			<# } #>
 		</li>
 		<?php
 	}
Index: src/wp-includes/class-wp-customize-section.php
===================================================================
--- src/wp-includes/class-wp-customize-section.php	(revision 30975)
+++ src/wp-includes/class-wp-customize-section.php	(working copy)
@@ -215,7 +215,11 @@
 	 * @return array The array to be exported to the client as JSON.
 	 */
 	public function json() {
-		$array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) );
+		if ( ! $this->check_capabilities() ) {
+			return;
+		}
+
+		$array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'panel', 'type' ) );
 		$array['content'] = $this->get_content();
 		$array['active'] = $this->active();
 		$array['instanceNumber'] = $this->instance_number;
@@ -243,7 +247,7 @@
 	}
 
 	/**
-	 * Get the section's content template for insertion into the Customizer pane.
+	 * Get the section's content for insertion into the Customizer pane.
 	 *
 	 * @since 4.1.0
 	 *
@@ -289,24 +293,56 @@
 	}
 
 	/**
-	 * Render the section, and the controls that have been added to it.
+	 * Render the section UI in a subclass.
 	 *
+	 * Sections are now rendered in JS by default, see {@see WP_Customize_Section::print_template()}.
+	 *
 	 * @since 3.4.0
 	 */
-	protected function render() {
-		$classes = 'accordion-section control-section control-section-' . $this->type;
+	protected function render() {}
+
+	/**
+	 * Render the section's JS template.
+	 *
+	 * This function is only run for section types that have been registered with
+	 * {@see WP_Customize_Manager::register_section_type()}.
+	 *
+	 * In the future, this will also print the template for the control's container
+	 * element and be override-able.
+	 *
+	 * @since 4.2.0
+	 */
+	public function print_template() {
+        ?>
+        <script type="text/html" id="tmpl-customize-section-<?php echo $this->type; ?>">
+			<?php $this->render_template(); ?>
+        </script>
+        <?php
+	}
+
+	/**
+	 * An Underscore (JS) template for rendering this section.
+	 *
+	 * Class variables for this section class are available in the `data` JS object;
+	 * export custom variables by overriding {@see WP_Customize_Section::json()}.
+	 *
+	 * @see WP_Customize_Section::print_template()
+	 *
+	 * @since 4.2.0
+	 */
+	protected function render_template() {
 		?>
-		<li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
+		<li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
 			<h3 class="accordion-section-title" tabindex="0">
-				<?php echo esc_html( $this->title ); ?>
+				{{ data.title }}
 				<span class="screen-reader-text"><?php _e( 'Press return or enter to expand' ); ?></span>
 			</h3>
 			<ul class="accordion-section-content">
-				<?php if ( ! empty( $this->description ) ) : ?>
+				<# if ( data.description ) { #>
 					<li class="customize-section-description-container">
-						<p class="description customize-section-description"><?php echo $this->description; ?></p>
+						<p class="description customize-section-description">{{{ data.description }}}</p>
 					</li>
-				<?php endif; ?>
+				<# } #>
 			</ul>
 		</li>
 		<?php
