diff --git a/typo3/sysext/backend/Classes/Controller/BackendController.php b/typo3/sysext/backend/Classes/Controller/BackendController.php
index fb5c0fcf87d83c2685ef5c4a0f4ba8829ceafa22..ba7be9039182efae124966c403003de4a69d75a1 100644
--- a/typo3/sysext/backend/Classes/Controller/BackendController.php
+++ b/typo3/sysext/backend/Classes/Controller/BackendController.php
@@ -289,24 +289,23 @@ class BackendController
         $this->generateJavascript();
         $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, false);
         $this->loadResourcesForRegisteredNavigationComponents();
-
-        // Add state provider
-        $this->getDocumentTemplate()->setExtDirectStateProvider();
-        $states = $this->getBackendUser()->uc['BackendComponents']['States'];
-        // Save states in BE_USER->uc
-        $extOnReadyCode = '
-			Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({
-				key: "BackendComponents.States",
-				autoRead: false
-			}));
-		';
-
-        if ($states) {
-            $extOnReadyCode .= 'Ext.state.Manager.getProvider().initState(' . json_encode($states) . ');';
-        }
-
-        $this->pageRenderer->addExtOnReadyCode($extOnReadyCode);
-
+        // @todo: remove this when ExtJS is removed
+        $this->pageRenderer->addExtOnReadyCode('
+            var TYPO3ExtJSStateProviderBridge = function() {};
+            Ext.extend(TYPO3ExtJSStateProviderBridge, Ext.state.Provider, {
+                prefix: "BackendComponents.States.",
+                get: function(name, defaultValue) {
+                    return TYPO3.Storage.Persistent.isset(this.prefix + name) ? TYPO3.Storage.Persistent.get(this.prefix + name) : defaultValue;
+                },
+                clear: function(name) {
+                    TYPO3.Storage.Persistent.unset(this.prefix + name);
+                },
+                set: function(name, value) {
+                    TYPO3.Storage.Persistent.set(this.prefix + name, value);
+                }
+            });
+            Ext.state.Manager.setProvider(new TYPO3ExtJSStateProviderBridge());
+	        ');
         // Set document title:
         $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version;
         // Renders the module page
diff --git a/typo3/sysext/backend/Classes/InterfaceState/ExtDirect/DataProvider.php b/typo3/sysext/backend/Classes/InterfaceState/ExtDirect/DataProvider.php
deleted file mode 100644
index b8bcae0c1a5a76efdbb0ed9ec33566b5e854ddd4..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Classes/InterfaceState/ExtDirect/DataProvider.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\InterfaceState\ExtDirect;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * ExtDirect DataProvider for State
- */
-class DataProvider
-{
-    /**
-     * @var \TYPO3\CMS\Backend\Controller\UserSettingsController
-     */
-    protected $userSettingsController;
-
-    /**
-     * Constructor
-     */
-    public function __construct()
-    {
-        // All data is saved in BE_USER->uc
-        $this->userSettingsController = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
-            \TYPO3\CMS\Backend\Controller\UserSettingsController::class
-        );
-    }
-
-    /**
-     * Gets state for given key
-     *
-     * @param \stdClass $parameter
-     * @return array
-     */
-    public function getState($parameter)
-    {
-        $key = $parameter->params->key;
-        $data = $this->userSettingsController->process('get', $key);
-        return [
-            'success' => true,
-            'data' => $data
-        ];
-    }
-
-    /**
-     * Save the state for a given key
-     *
-     * @param \stdClass $parameter
-     * @return array
-     */
-    public function setState($parameter)
-    {
-        $key = $parameter->params->key;
-        $data = json_decode($parameter->params->data);
-        foreach ($data as $setting) {
-            $this->userSettingsController->process('set', $key . '.' . $setting->name, $setting->value);
-        }
-        return [
-            'success' => true,
-            'params' => $parameter
-        ];
-    }
-}
diff --git a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
index e4c5bdbf3f0953924b3aa2834cac4790bc71dcf8..1468ce28ff7d033a6f6ec87440486e17de21fd95 100644
--- a/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
+++ b/typo3/sysext/backend/Classes/Template/DocumentTemplate.php
@@ -234,11 +234,6 @@ function jumpToUrl(URL) {
      */
     protected $pageHeaderFooterTemplateFile = '';
 
-    /**
-     * @var bool
-     */
-    protected $extDirectStateProvider = false;
-
     /**
      * Whether flashmessages should be rendered or not
      *
@@ -338,16 +333,6 @@ function jumpToUrl(URL) {
         }
     }
 
-    /**
-     * Sets inclusion of StateProvider
-     *
-     * @return void
-     */
-    public function setExtDirectStateProvider()
-    {
-        $this->extDirectStateProvider = true;
-    }
-
     /*****************************************
      *
      * EVALUATION FUNCTIONS
@@ -614,9 +599,6 @@ function jumpToUrl(URL) {
         $this->pageRenderer->setTitle($title);
         // add docstyles
         $this->docStyle();
-        if ($this->extDirectStateProvider) {
-            $this->pageRenderer->addJsFile('EXT:backend/Resources/Public/JavaScript/ExtDirect.StateProvider.js');
-        }
         $this->pageRenderer->addHeaderData($this->JScode);
         foreach ($this->JScodeArray as $name => $code) {
             $this->pageRenderer->addJsInlineCode($name, $code, false);
diff --git a/typo3/sysext/backend/Classes/Tree/AbstractExtJsTree.php b/typo3/sysext/backend/Classes/Tree/AbstractExtJsTree.php
deleted file mode 100644
index e7ceb3d72a90173761b5d7bdc187433eab01ee5c..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Classes/Tree/AbstractExtJsTree.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\Tree;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Abstract ExtJS tree based on ExtDirect
- */
-abstract class AbstractExtJsTree extends \TYPO3\CMS\Backend\Tree\AbstractTree
-{
-    /**
-     * State Provider
-     *
-     * @var \TYPO3\CMS\Backend\Tree\AbstractTreeStateProvider
-     */
-    protected $stateProvider = null;
-
-    /**
-     * @param \TYPO3\CMS\Backend\Tree\AbstractTreeStateProvider $stateProvider
-     * @return void
-     */
-    public function setStateProvider(\TYPO3\CMS\Backend\Tree\AbstractTreeStateProvider $stateProvider)
-    {
-        $this->stateProvider = $stateProvider;
-    }
-
-    /**
-     * @return \TYPO3\CMS\Backend\Tree\AbstractTreeStateProvider
-     */
-    public function getStateProvider()
-    {
-        return $this->stateProvider;
-    }
-
-    /**
-     * Fetches the next tree level
-     *
-     * @param int $nodeId
-     * @param stdClass $nodeData
-     * @return array
-     */
-    abstract public function getNextTreeLevel($nodeId, $nodeData);
-}
diff --git a/typo3/sysext/backend/Classes/Tree/AbstractTreeStateProvider.php b/typo3/sysext/backend/Classes/Tree/AbstractTreeStateProvider.php
deleted file mode 100644
index 46d320506363d23e6823121f643d3183c645ee14..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Classes/Tree/AbstractTreeStateProvider.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-namespace TYPO3\CMS\Backend\Tree;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-/**
- * Abstract State Provider
- *
- * @todo This class is incomplete, because the methods still need
- */
-abstract class AbstractTreeStateProvider
-{
-    /**
-     * Sets the current tree state
-     *
-     * @return void
-     */
-    abstract public function setState();
-
-    /**
-     * Returns the last tree state
-     *
-     * @return something
-     */
-    abstract public function getState();
-}
diff --git a/typo3/sysext/backend/Classes/Tree/Pagetree/ExtdirectTreeDataProvider.php b/typo3/sysext/backend/Classes/Tree/Pagetree/ExtdirectTreeDataProvider.php
index 0ac6b4894106aeef063136a8374949a10395eada..638de8793b75c7b63c77473506f7b9faab6fbef7 100644
--- a/typo3/sysext/backend/Classes/Tree/Pagetree/ExtdirectTreeDataProvider.php
+++ b/typo3/sysext/backend/Classes/Tree/Pagetree/ExtdirectTreeDataProvider.php
@@ -22,7 +22,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 /**
  * Data Provider of the Page Tree
  */
-class ExtdirectTreeDataProvider extends \TYPO3\CMS\Backend\Tree\AbstractExtJsTree
+class ExtdirectTreeDataProvider extends \TYPO3\CMS\Backend\Tree\AbstractTree
 {
     /**
      * Data Provider
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/ExtDirect.StateProvider.js b/typo3/sysext/backend/Resources/Public/JavaScript/ExtDirect.StateProvider.js
deleted file mode 100644
index 978a7d118bad5116902b1a3a234efbb9a21b08f5..0000000000000000000000000000000000000000
--- a/typo3/sysext/backend/Resources/Public/JavaScript/ExtDirect.StateProvider.js
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-Ext.ns('TYPO3.state');
-
-/**
- * Creates new ExtDirectProvider
- * @constructor
- * @param {Object} config Configuration object
- */
-
-TYPO3.state.ExtDirectProvider = function(config) {
-
-	this.addEvents(
-		/**
-		 * @event readsuccess
-		 * Fires after state has been successfully received from server and restored
-		 * @param {HttpProvider} this
-		 */
-			'readsuccess',
-		/**
-		 * @event readfailure
-		 * Fires in the case of an error when attempting to read state from server
-		 * @param {HttpProvider} this
-		 */
-			'readfailure',
-		/**
-		 * @event savesuccess
-		 * Fires after the state has been successfully saved to server
-		 * @param {HttpProvider} this
-		 */
-			'savesuccess',
-		/**
-		 * @event savefailure
-		 * Fires in the case of an error when attempting to save state to the server
-		 * @param {HttpProvider} this
-		 */
-			'savefailure'
-	);
-
-		// call parent
-	TYPO3.state.ExtDirectProvider.superclass.constructor.call(this);
-
-	Ext.apply(this, config, {
-		// defaults
-		delay: 750, // buffer changes for 750 ms
-		dirty: false,
-		started: false,
-		autoStart: true,
-		autoRead: true,
-		key: 'States.General',
-		logFailure: false,
-		logSuccess: false,
-		queue: [],
-		saveBaseParams: {},
-		readBaseParams: {},
-		paramNames:{
-			key: 'key',
-			name: 'name',
-			value: 'value',
-			data: 'data'
-		}
-	});
-
-	if (this.autoRead) {
-		this.readState();
-	}
-
-	this.dt = new Ext.util.DelayedTask(this.submitState, this);
-	if (this.autoStart) {
-		this.start();
-	}
-};
-
-
-Ext.extend(TYPO3.state.ExtDirectProvider, Ext.state.Provider, {
-
-		// localizable texts
-	saveSuccessText: 'Save Success',
-	saveFailureText: 'Save Failure',
-	readSuccessText: 'Read Success',
-	readFailureText: 'Read Failure',
-	dataErrorText: 'Data Error',
-
-
-
-	/**
-	 * Initializes state from the passed state object or array.
-	 * Use this with loading page using initial state in TYPO3.settings
-	 *
-	 * @param {Array/Object} state State to initialize state manager with
-	 */
-	initState: function(state) {
-		if (Ext.isArray(state)) {
-			Ext.each(state, function(item) {
-				this.state[item.name] = item[this.paramNames.value];
-			}, this);
-		} else if (Ext.isObject(state)) {
-			Ext.iterate(state, function(key, value){
-				this.state[key] = value;
-			}, this);
-		} else {
-			this.state = {};
-		}
-	},
-
-	/**
-	 * Sets the passed state variable name to the passed value and queues the change
-	 * @param {String} name Name of the state variable
-	 * @param {Mixed} value Value of the state variable
-	 */
-	set: function(name, value) {
-		if (!name) {
-			return;
-		}
-		this.queueChange(name, value);
-	},
-
-
-	/**
-	 * Starts submitting state changes to server
-	 */
-	start: function() {
-		this.dt.delay(this.delay);
-		this.started = true;
-	},
-
-
-	/**
-	 * Stops submitting state changes
-	 */
-	stop: function() {
-		this.dt.cancel();
-		this.started = false;
-	},
-
-
-	/**
-	 * private, queues the state change if state has changed
-	 */
-	queueChange: function(name, value) {
-		var o = {};
-		var i;
-		var found = false;
-
-		var lastValue = this.state[name];
-		for (i = 0; i < this.queue.length; i++) {
-			if (this.queue[i].name === name) {
-				lastValue = this.queue[i].value;
-			}
-		}
-		var changed = undefined === lastValue || lastValue !== value;
-
-		if (changed) {
-			o[this.paramNames.name] = name;
-			o[this.paramNames.value] = value;
-			for (i = 0; i < this.queue.length; i++) {
-				if (this.queue[i].name === o.name) {
-					this.queue[i] = o;
-					found = true;
-				}
-			}
-			if (false === found) {
-				this.queue.push(o);
-			}
-			this.dirty = true;
-		}
-		if (this.started) {
-			this.start();
-		}
-		return changed;
-	},
-
-
-	/**
-	 * private, submits state to server by asynchronous Ajax request
-	 */
-	submitState: function() {
-		if (!this.dirty) {
-			this.dt.delay(this.delay);
-			return;
-		}
-		this.dt.cancel();
-
-		var o = {
-			scope: this,
-			success: this.onSaveSuccess,
-			failure: this.onSaveFailure,
-			queue: this.queue, //this.clone(this.queue),
-			params: {}
-		};
-
-		var params = Ext.apply({}, this.saveBaseParams);
-		params[this.paramNames.key] = this.key;
-		params[this.paramNames.data] = Ext.encode(o.queue);
-
-		Ext.apply(o.params, params);
-
-		// be optimistic
-		this.dirty = false;
-
-	   TYPO3.ExtDirectStateProvider.ExtDirect.setState(o, function(response, options) {
-		   if (response.success) {
-				this.onSaveSuccess(response, options);
-		   } else {
-				this.onSaveFailure(response, options);
-		   }
-	   }, this);
-	},
-
-
-	/**
-	 * Clears the state variable
-	 * @param {String} name Name of the variable to clear
-	 */
-	clear: function(name) {
-		this.set(name, undefined);
-	},
-
-
-	/**
-	 * private, save success callback
-	 */
-	onSaveSuccess: function(response, options) {
-		var o = response;
-		if (!o.success) {
-			if (this.logFailure) {
-				this.log(this.saveFailureText, o, response);
-			}
-			this.dirty = true;
-		} else {
-			Ext.each(response.params.queue, function(item) {
-				if (!item) {
-					return;
-				}
-				var name = item[this.paramNames.name];
-				var value = item[this.paramNames.value];
-
-				if (value === undefined || value === null) {
-					TYPO3.state.ExtDirectProvider.superclass.clear.call(this, name);
-				} else {
-						// parent sets value and fires event
-					TYPO3.state.ExtDirectProvider.superclass.set.call(this, name, value);
-				}
-			}, this);
-			if (!this.dirty) {
-				this.queue = [];
-			}else {
-				var i, j, found;
-				for (i = 0; i < response.params.queue.length; i++) {
-					found = false;
-					for (j = 0; j < this.queue.length; j++) {
-						if (response.params.queue[i].name === this.queue[j].name) {
-							found = true;
-							break;
-						}
-					}
-					if (found && response.params.queue[i].value === this.queue[j].value) {
-						this.queue.remove(this.queue[j]);
-					}
-				}
-			}
-			if (this.logSuccess) {
-				this.log(this.saveSuccessText, o, response);
-			}
-			this.fireEvent('savesuccess', this);
-		}
-	},
-
-
-	/**
-	 * private, save failure callback
-	 */
-	onSaveFailure: function(response, options) {
-		if (true === this.logFailure) {
-			this.log(this.saveFailureText, response);
-		}
-		this.dirty = true;
-		this.fireEvent('savefailure', this);
-	},
-
-
-	/**
-	 * private, read state callback
-	 */
-	onReadFailure: function(response, options) {
-		if (this.logFailure) {
-			this.log(this.readFailureText, response);
-		}
-		this.fireEvent('readfailure', this);
-
-	},
-
-
-	/**
-	 * private, read success callback
-	 */
-	onReadSuccess: function(response, options) {
-		var o = response, data;
-		if (!o.success) {
-			if (this.logFailure) {
-				this.log(this.readFailureText, o, response);
-			}
-		} else {
-			data = o[this.paramNames.data];
-			Ext.iterate(data, function(key, value) {
-				this.state[key] = value;
-			}, this);
-			this.queue = [];
-			this.dirty = false;
-			if (this.logSuccess) {
-				this.log(this.readSuccessText, data, response);
-			}
-			this.fireEvent('readsuccess', this);
-		}
-	},
-
-
-	/**
-	 * Reads saved state from server by sending asynchronous Ajax request and processing the response
-	 */
-	readState: function() {
-		var o = {
-			scope: this,
-			params:{}
-		};
-
-		var params = Ext.apply({}, this.readBaseParams);
-		params[this.paramNames.key] = this.key;
-
-		Ext.apply(o.params, params);
-		TYPO3.ExtDirectStateProvider.ExtDirect.getState(o, function(response, options) {
-		   if (response.success) {
-				this.onReadSuccess(response, options);
-		   } else {
-				this.onReadFailure(response, options);
-		   }
-	   }, this);
-	},
-
-
-	/**
-	 * private, logs errors or successes
-	 */
-	log: function() {
-		if (console) {
-			console.log.apply(console, arguments);
-		}
-	},
-
-	logState: function() {
-	   if (console) {
-			console.log(this.state);
-		}
-	}
-
-});
diff --git a/typo3/sysext/core/Classes/Core/Bootstrap.php b/typo3/sysext/core/Classes/Core/Bootstrap.php
index 4b95514cf93deda2a6e7220bc40c8db8cad2c010..2ad8ab6d4dba70142e59ee707a56240a41600f4b 100644
--- a/typo3/sysext/core/Classes/Core/Bootstrap.php
+++ b/typo3/sysext/core/Classes/Core/Bootstrap.php
@@ -544,10 +544,6 @@ class Bootstrap
                 'TYPO3.Components.PageTree.ContextMenuDataProvider',
                 \TYPO3\CMS\Backend\ContextMenu\Pagetree\Extdirect\ContextMenuConfiguration::class
             );
-            ExtensionManagementUtility::registerExtDirectComponent(
-                'TYPO3.ExtDirectStateProvider.ExtDirect',
-                \TYPO3\CMS\Backend\InterfaceState\ExtDirect\DataProvider::class
-            );
         }
         return $this;
     }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-79227-RemovedExtDirectStateProvider.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-79227-RemovedExtDirectStateProvider.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bdca2e8db16177aa398c97f1ca086b1c170d5108
--- /dev/null
+++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-79227-RemovedExtDirectStateProvider.rst
@@ -0,0 +1,51 @@
+.. include:: ../../Includes.txt
+
+===================================================
+Breaking: #79227 - Removed ExtDirect State Provider
+===================================================
+
+See :issue:`79227`
+
+Description
+===========
+
+The ExtDirect based State Provider for ExtJS applications (endpoint `TYPO3.ExtDirectStateProvider.ExtDirect`) has been removed.
+
+The ExtDirect endpoint `TYPO3.ExtDirectStateProvider.ExtDirect` is no longer available.
+
+The following PHP classes have been removed:
+* `\TYPO3\CMS\Backend\InterfaceState\ExtDirect\DataProvider`
+* `\TYPO3\CMS\Backend\Tree\AbstractTreeStateProvider`
+* `\TYPO3\CMS\Backend\Tree\AbstractExtJsTree`
+
+The relevant JavaScript file `ExtDirect.StateProvider.js` has been removed.
+
+The PHP method php:`DocumentTemplate->setExtDirectStateProvider()` to load the JavaScript file has been removed.
+
+Instead the jQuery-based AMD module `TYPO3\CMS\Backend\Storage` is incorporated to load the data the same way via an anonymous
+State Provider which is handed to ExtJS as long as ExtJS is still available in the TYPO3 Core.
+
+
+Impact
+======
+
+Accessing the ExtDirect endpoint will result in a JavaScript error. Loading the JavaScript file will result in a HTTP 404 error.
+
+Instantiating the PHP class will result in a fatal PHP error.
+
+
+Affected Installations
+======================
+
+Any installation using custom implementations with ExtDirect and the State Provider shipped with the TYPO3 Core.
+
+
+Migration
+=========
+
+Include the `TYPO3\CMS\Backend\Storage`, and use the UserSettingsController class directly on the PHP side to
+access the user settings.
+
+See the implementation of the JavaScript Storage object for a more detailed usage.
+
+.. index:: JavaScript