diff --git src/wp-admin/js/widgets/text-widgets.js src/wp-admin/js/widgets/text-widgets.js
index 2108b65548..5fec0ac489 100644
--- src/wp-admin/js/widgets/text-widgets.js
+++ src/wp-admin/js/widgets/text-widgets.js
@@ -233,6 +233,11 @@ wp.textWidgets = ( function( $ ) {
 			return;
 		}
 
+		// Bypass using TinyMCE when widget is in legacy mode.
+		if ( widgetForm.find( '.legacy' ).length > 0 ) {
+			return;
+		}
+
 		widgetControl = new component.TextWidgetControl({
 			el: widgetContainer
 		});
diff --git src/wp-includes/widgets/class-wp-widget-text.php src/wp-includes/widgets/class-wp-widget-text.php
index 7d149c0a0f..4d93935a3a 100644
--- src/wp-includes/widgets/class-wp-widget-text.php
+++ src/wp-includes/widgets/class-wp-widget-text.php
@@ -53,6 +53,114 @@ class WP_Widget_Text extends WP_Widget {
 	}
 
 	/**
+	 * Determines whether a given instance is legacy and should bypass using TinyMCE.
+	 *
+	 * @since 4.8.1
+	 *
+	 * @param array $instance {
+	 *     Instance data.
+	 *
+	 *     @type string      $text   Content.
+	 *     @type bool|string $filter Whether autop or content filters should apply.
+	 *     @type bool        $legacy Whether widget is in legacy mode.
+	 * }
+	 * @return bool Whether Text widget instance contains legacy data.
+	 */
+	public function is_legacy_instance( $instance ) {
+
+		// If the widget has been updated while in legacy mode, it stays in legacy mode.
+		if ( ! empty( $instance['legacy'] ) ) {
+			return true;
+		}
+
+		// If the widget has been added/updated in 4.8 then filter prop is 'content' and it is no longer legacy.
+		if ( isset( $instance['filter'] ) && 'content' === $instance['filter'] ) {
+			return false;
+		}
+
+		// If the text is empty, then nothing is preventing migration to TinyMCE.
+		if ( empty( $instance['text'] ) ) {
+			return false;
+		}
+
+		$wpautop = ! empty( $instance['filter'] );
+
+		// If auto-paragraphs are not enabled and there are line breaks, then ensure legacy mode.
+		if ( ! $wpautop && false === strpos( "\n", $instance['text'] ) ) {
+			return true;
+		}
+
+		// In the rare case that DOMDocument is not available we cannot reliably sniff content and so we assume legacy.
+		if ( ! class_exists( 'DOMDocument' ) ) {
+			// @codeCoverageIgnoreStart
+			return true;
+			// @codeCoverageIgnoreEnd
+		}
+
+		$doc = new DOMDocument();
+		$doc->loadHTML( sprintf(
+			'<html><head><meta charset="%s"></head><body>%s</body></html>',
+			esc_attr( get_bloginfo( 'charset' ) ),
+			$instance['text']
+		) );
+		$body = $doc->getElementsByTagName( 'body' )->item( 0 );
+
+		// See $allowedposttags.
+		$safe_elements_attributes = array(
+			'strong' => array(),
+			'em' => array(),
+			'b' => array(),
+			'i' => array(),
+			'u' => array(),
+			's' => array(),
+			'ul' => array(),
+			'ol' => array(),
+			'li' => array(),
+			'hr' => array(),
+			'abbr' => array(),
+			'acronym' => array(),
+			'code' => array(),
+			'dfn' => array(),
+			'a' => array(
+				'href' => true,
+			),
+			'img' => array(
+				'src' => true,
+				'alt' => true,
+			),
+		);
+		$safe_empty_elements = array( 'img', 'hr', 'iframe' );
+
+		foreach ( $body->getElementsByTagName( '*' ) as $element ) {
+			/** @var DOMElement $element */
+			$tag_name = strtolower( $element->nodeName );
+
+			// If the element is not safe, then the instance is legacy.
+			if ( ! isset( $safe_elements_attributes[ $tag_name ] ) ) {
+				return true;
+			}
+
+			// If the element is not safely empty and it has empty contents, then legacy mode.
+			if ( ! in_array( $tag_name, $safe_empty_elements, true ) && '' === trim( $element->textContent ) ) {
+				return true;
+			}
+
+			// If an attribute is not recognized as safe, then the instance is legacy.
+			foreach ( $element->attributes as $attribute ) {
+				/** @var DOMAttr $attribute */
+				$attribute_name = strtolower( $attribute->nodeName );
+
+				if ( ! isset( $safe_elements_attributes[ $tag_name ][ $attribute_name ] ) ) {
+					return true;
+				}
+			}
+		}
+
+		// Otherwise, the text contains no elements/attributes that TinyMCE could drop, and therefore the widget does not need legacy mode.
+		return false;
+	}
+
+	/**
 	 * Outputs the content for the current Text widget instance.
 	 *
 	 * @since 2.8.0
@@ -134,12 +242,20 @@ class WP_Widget_Text extends WP_Widget {
 		}
 
 		/*
-		 * Re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply.
+		 * If the Text widget is in legacy mode, then a hidden input will indicate this
+		 * and the new content value for the filter prop will by bypassed. Otherwise,
+		 * re-use legacy 'filter' (wpautop) property to now indicate content filters will always apply.
 		 * Prior to 4.8, this is a boolean value used to indicate whether or not wpautop should be
 		 * applied. By re-using this property, downgrading WordPress from 4.8 to 4.7 will ensure
 		 * that the content for Text widgets created with TinyMCE will continue to get wpautop.
 		 */
-		$instance['filter'] = 'content';
+		if ( isset( $new_instance['legacy'] ) || isset( $old_instance['legacy'] ) || ( isset( $new_instance['filter'] ) && 'content' !== $new_instance['filter'] ) ) {
+			$instance['filter'] = ! empty( $new_instance['filter'] );
+			$instance['legacy'] = true;
+		} else {
+			$instance['filter'] = 'content';
+			unset( $instance['legacy'] );
+		}
 
 		return $instance;
 	}
@@ -160,6 +276,7 @@ class WP_Widget_Text extends WP_Widget {
 	 *
 	 * @since 2.8.0
 	 * @since 4.8.0 Form only contains hidden inputs which are synced with JS template.
+	 * @since 4.8.1 Restored original form to be displayed when in legacy mode.
 	 * @access public
 	 * @see WP_Widget_Visual_Text::render_control_template_scripts()
 	 *
@@ -175,9 +292,26 @@ class WP_Widget_Text extends WP_Widget {
 			)
 		);
 		?>
-		<input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>">
-		<input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>">
-		<?php
+		<?php if ( ! $this->is_legacy_instance( $instance ) ) : ?>
+			<input id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" class="title" type="hidden" value="<?php echo esc_attr( $instance['title'] ); ?>">
+			<input id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>" class="text" type="hidden" value="<?php echo esc_attr( $instance['text'] ); ?>">
+		<?php else : ?>
+			<input name="<?php echo $this->get_field_name( 'legacy' ); ?>" type="hidden" class="legacy" value="true">
+			<p>
+				<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
+				<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>"/>
+			</p>
+			<div class="notice notice-info notice-alt">
+				<p><?php _e( 'This widget contains code that may be better used in the HTML Code widget. We recommend you use the HTML Code widget for this content.' ); ?></p>
+			</div>
+			<p>
+				<label for="<?php echo $this->get_field_id( 'text' ); ?>"><?php _e( 'Content:' ); ?></label>
+				<textarea class="widefat" rows="16" cols="20" id="<?php echo $this->get_field_id( 'text' ); ?>" name="<?php echo $this->get_field_name( 'text' ); ?>"><?php echo esc_textarea( $instance['text'] ); ?></textarea>
+			</p>
+			<p>
+				<input id="<?php echo $this->get_field_id( 'filter' ); ?>" name="<?php echo $this->get_field_name( 'filter' ); ?>" type="checkbox"<?php checked( ! empty( $instance['filter'] ) ); ?> />&nbsp;<label for="<?php echo $this->get_field_id( 'filter' ); ?>"><?php _e( 'Automatically add paragraphs' ); ?></label>
+			</p>
+		<?php endif;
 	}
 
 	/**
diff --git tests/phpunit/tests/widgets/text-widget.php tests/phpunit/tests/widgets/text-widget.php
index a28e6a668a..074b42b201 100644
--- tests/phpunit/tests/widgets/text-widget.php
+++ tests/phpunit/tests/widgets/text-widget.php
@@ -40,6 +40,20 @@ class Test_WP_Widget_Text extends WP_UnitTestCase {
 	}
 
 	/**
+	 * Test constructor method.
+	 *
+	 * @covers WP_Widget_Text::__construct
+	 */
+	function test_construct() {
+		$widget = new WP_Widget_Text();
+		$this->assertEquals( 'text', $widget->id_base );
+		$this->assertEquals( 'widget_text', $widget->widget_options['classname'] );
+		$this->assertTrue( $widget->widget_options['customize_selective_refresh'] );
+		$this->assertEquals( 400, $widget->control_options['width'] );
+		$this->assertEquals( 350, $widget->control_options['height'] );
+	}
+
+	/**
 	 * Test enqueue_admin_scripts method.
 	 *
 	 * @covers WP_Widget_Text::_register
@@ -152,6 +166,135 @@ class Test_WP_Widget_Text extends WP_UnitTestCase {
 	}
 
 	/**
+	 * Test is_legacy_instance method.
+	 *
+	 * @covers WP_Widget_Text::is_legacy_instance
+	 */
+	function test_is_legacy_instance() {
+		$widget = new WP_Widget_Text();
+		$base_instance = array(
+			'title' => 'Title',
+			'text' => "Hello\n\nWorld",
+		);
+
+		$instance = array_merge( $base_instance, array(
+			'legacy' => true,
+		) );
+		$this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when legacy prop is present.' );
+
+		$instance = array_merge( $base_instance, array(
+			'filter' => 'content',
+		) );
+		$this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when filter is explicitly content.' );
+
+		$instance = array_merge( $base_instance, array(
+			'text' => '',
+			'filter' => true,
+		) );
+		$this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not legacy when text is empty.' );
+
+		$instance = array_merge( $base_instance, array(
+			'text' => "One\nTwo",
+			'filter' => false,
+		) );
+		$this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there are line breaks.' );
+
+		$instance = array_merge( $base_instance, array(
+			'text' => "One\n\nTwo",
+			'filter' => false,
+		) );
+		$this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there are paragraph breaks.' );
+
+		$instance = array_merge( $base_instance, array(
+			'text' => "One\nTwo",
+			'filter' => true,
+		) );
+		$this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not automatically legacy when wpautop and there are line breaks.' );
+
+		$instance = array_merge( $base_instance, array(
+			'text' => "One\n\nTwo",
+			'filter' => true,
+		) );
+		$this->assertFalse( $widget->is_legacy_instance( $instance ), 'Not automatically legacy when wpautop and there are paragraph breaks.' );
+
+		// Check text examples that will not migrate to TinyMCE.
+		$legacy_text_examples = array(
+			'<span class="hello"></span>',
+			'<span></span>',
+			"<ul>\n<li><a href=\"#\" class=\"location\"></a>List Item 1</li>\n<li><a href=\"#\" class=\"location\"></a>List Item 2</li>\n</ul>",
+			'<a href="#" class="map"></a>',
+			"<script>\n\\Line one\n\n\\Line two</script>",
+			"<style>body {\ncolor:red;\n}</style>",
+			'<span class="fa fa-cc-discover fa-2x" aria-hidden="true"></span>',
+			"<p>\nStay updated with our latest news and specials. We never sell your information and you can unsubscribe at any time.\n</p>\n\n<div class=\"custom-form-class\">\n\t<form action=\"#\" method=\"post\" name=\"mc-embedded-subscribe-form\">\n\n\t\t<label class=\"screen-reader-text\" for=\"mce-EMAIL-b\">Email </label>\n\t\t<input id=\"mce-EMAIL-b\" class=\"required email\" name=\"EMAIL\" required=\"\" type=\"email\" value=\"\" placeholder=\"Email Address*\" />\n\n\t\t<input class=\"button\" name=\"subscribe\" type=\"submit\" value=\"Go!\" />\n\n\t</form>\n</div>",
+			'<span class="sectiondown"><a href="#front-page-3"><i class="fa fa-chevron-circle-down"></i></a></span>',
+		);
+		foreach ( $legacy_text_examples as $legacy_text_example ) {
+			$instance = array_merge( $base_instance, array(
+				'text' => $legacy_text_example,
+				'filter' => true,
+			) );
+			$this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' );
+
+			$instance = array_merge( $base_instance, array(
+				'text' => $legacy_text_example,
+				'filter' => false,
+			) );
+			$this->assertTrue( $widget->is_legacy_instance( $instance ), 'Legacy when not-wpautop and there is HTML that is not liable to be mutated.' );
+		}
+
+		// Check text examples that will migrate to TinyMCE, where elements and attributes are not in whitelist.
+		$migratable_text_examples = array(
+			'Check out <a href="http://example.com">Example</a>',
+			'<img src="http://example.com/img.jpg" alt="Img">',
+			'<strong><em>Hello</em></strong>',
+			'<b><i><u><s>Hello</s></u></i></b>',
+			"<ul>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ul>",
+			"<ol>\n<li>One</li>\n<li>One</li>\n<li>One</li>\n</ol>",
+			"Text\n<hr>\nAddendum",
+			"Look at this code:\n\n<code>echo 'Hello World!';</code>",
+		);
+		foreach ( $migratable_text_examples as $migratable_text_example ) {
+			$instance = array_merge( $base_instance, array(
+				'text' => $migratable_text_example,
+				'filter' => true,
+			) );
+			$this->assertFalse( $widget->is_legacy_instance( $instance ), 'Legacy when wpautop and there is HTML that is not liable to be mutated.' );
+		}
+	}
+
+	/**
+	 * Test update method.
+	 *
+	 * @covers WP_Widget_Text::form
+	 */
+	function test_form() {
+		$widget = new WP_Widget_Text();
+		$instance = array(
+			'title' => 'Title',
+			'text' => 'Text',
+			'filter' => false,
+			'legacy' => true,
+		);
+		$this->assertTrue( $widget->is_legacy_instance( $instance ) );
+		ob_start();
+		$widget->form( $instance );
+		$form = ob_get_clean();
+		$this->assertContains( 'class="legacy"', $form );
+
+		$instance = array(
+			'title' => 'Title',
+			'text' => 'Text',
+			'filter' => 'content',
+		);
+		$this->assertFalse( $widget->is_legacy_instance( $instance ) );
+		ob_start();
+		$widget->form( $instance );
+		$form = ob_get_clean();
+		$this->assertNotContains( 'class="legacy"', $form );
+	}
+
+	/**
 	 * Test update method.
 	 *
 	 * @covers WP_Widget_Text::update
@@ -161,21 +304,21 @@ class Test_WP_Widget_Text extends WP_UnitTestCase {
 		$instance = array(
 			'title' => "The\nTitle",
 			'text'  => "The\n\nText",
-			'filter' => false,
+			'filter' => 'content',
 		);
 
 		wp_set_current_user( $this->factory()->user->create( array(
 			'role' => 'administrator',
 		) ) );
 
-		// Should return valid instance.
+		// Should return valid instance in legacy mode since filter=false and there are line breaks.
 		$expected = array(
 			'title'  => sanitize_text_field( $instance['title'] ),
 			'text'   => $instance['text'],
 			'filter' => 'content',
 		);
 		$result = $widget->update( $instance, array() );
-		$this->assertEquals( $result, $expected );
+		$this->assertEquals( $expected, $result );
 		$this->assertTrue( ! empty( $expected['filter'] ), 'Expected filter prop to be truthy, to handle case where 4.8 is downgraded to 4.7.' );
 
 		// Make sure KSES is applying as expected.
@@ -184,7 +327,7 @@ class Test_WP_Widget_Text extends WP_UnitTestCase {
 		$instance['text'] = '<script>alert( "Howdy!" );</script>';
 		$expected['text'] = $instance['text'];
 		$result = $widget->update( $instance, array() );
-		$this->assertEquals( $result, $expected );
+		$this->assertEquals( $expected, $result );
 		remove_filter( 'map_meta_cap', array( $this, 'grant_unfiltered_html_cap' ) );
 
 		add_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10, 2 );
@@ -192,11 +335,63 @@ class Test_WP_Widget_Text extends WP_UnitTestCase {
 		$instance['text'] = '<script>alert( "Howdy!" );</script>';
 		$expected['text'] = wp_kses_post( $instance['text'] );
 		$result = $widget->update( $instance, array() );
-		$this->assertEquals( $result, $expected );
+		$this->assertEquals( $expected, $result );
 		remove_filter( 'map_meta_cap', array( $this, 'revoke_unfiltered_html_cap' ), 10 );
 	}
 
 	/**
+	 * Test update for legacy widgets.
+	 *
+	 * @covers WP_Widget_Text::update
+	 */
+	function test_update_legacy() {
+		$widget = new WP_Widget_Text();
+
+		// Updating a widget with explicit filter=true persists with legacy mode.
+		$instance = array(
+			'title' => 'Legacy',
+			'text' => 'Text',
+			'filter' => true,
+		);
+		$result = $widget->update( $instance, array() );
+		$expected = array_merge( $instance, array(
+			'legacy' => true,
+			'filter' => true,
+		) );
+		$this->assertEquals( $expected, $result );
+
+		// Updating a widget with explicit filter=false persists with legacy mode.
+		$instance['filter'] = false;
+		$result = $widget->update( $instance, array() );
+		$expected = array_merge( $instance, array(
+			'legacy' => true,
+			'filter' => false,
+		) );
+		$this->assertEquals( $expected, $result );
+
+		// Updating a widget in legacy form results in filter=false when checkbox not checked.
+		$instance['filter'] = true;
+		$result = $widget->update( $instance, array() );
+		$expected = array_merge( $instance, array(
+			'legacy' => true,
+			'filter' => true,
+		) );
+		$this->assertEquals( $expected, $result );
+
+		// Updating a widget that previously had legacy form results in filter persisting.
+		unset( $instance['legacy'] );
+		$instance['filter'] = true;
+		$result = $widget->update( $instance, array(
+			'legacy' => true,
+		) );
+		$expected = array_merge( $instance, array(
+			'legacy' => true,
+			'filter' => true,
+		) );
+		$this->assertEquals( $expected, $result );
+	}
+
+	/**
 	 * Grant unfiltered_html cap via map_meta_cap.
 	 *
 	 * @param array  $caps    Returns the user's actual capabilities.
