diff --git a/.env.example b/.env.example
deleted file mode 100644
index e2ac444dd0..0000000000
--- a/.env.example
+++ /dev/null
@@ -1,16 +0,0 @@
-# To enable CSS hot-reload, set the following variable to 1.
-CSS_HOT_RELOAD=1
-# To use the full page error dialog, set this to 1. Please report false positives to
-# the issue tracker for handling.
-FULL_PAGE_ERRORS=0
-# To use a single theme, uncomment the line with the theme you want to hot-reload.
-MATRIX_THEMES='light'
-#MATRIX_THEMES='dark'
-#MATRIX_THEMES='legacy-light'
-#MATRIX_THEMES='legacy-dark'
-#MATRIX_THEMES='light-custom'
-#MATRIX_THEMES='dark-custom'
-# You can also enable multiple themes by using a comma-separated list.
-# When multiple themes are enabled, switching between them may require a full page reload.
-# Note that compilation times are proportional to the number of enabled themes.
-#MATRIX_THEMES='light,dark'
diff --git a/README.md b/README.md
index 19875e083d..deab264f86 100644
--- a/README.md
+++ b/README.md
@@ -206,10 +206,6 @@ internet. So please don't depend on resources (JS libs, CSS, images, fonts)
 hosted by external CDNs or servers but instead please package all dependencies
 into Element itself.
 
-CSS hot-reload is available as an opt-in development feature. You can enable it
-by defining a `CSS_HOT_RELOAD` environment variable, in a `.env` file in the root
-of the repository. See `.env.example` for documentation and an example.
-
 # Setting up a dev environment
 
 Much of the functionality in Element is actually in the `matrix-react-sdk` and
diff --git a/package.json b/package.json
index f0265a8068..67451cd31f 100644
--- a/package.json
+++ b/package.json
@@ -198,8 +198,6 @@
         "raw-loader": "^4.0.2",
         "rimraf": "^6.0.0",
         "semver": "^7.5.2",
-        "string-replace-loader": "3",
-        "style-loader": "4",
         "stylelint": "^16.1.0",
         "stylelint-config-standard": "^36.0.0",
         "stylelint-scss": "^6.0.0",
diff --git a/src/vector/devcss.ts b/src/vector/devcss.ts
deleted file mode 100644
index 38cca75ba3..0000000000
--- a/src/vector/devcss.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-Copyright 2024 New Vector Ltd.
-Copyright 2021 The Matrix.org Foundation C.I.C.
-
-SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
-Please see LICENSE files in the repository root for full details.
-*/
-
-/**
- * This code is removed on production builds.
- *
- * Webpack's `string-replace-loader` searches for the `use theming` string
- * in this specific file, and replaces it with CSS requires statements that
- * are specific to the themes we have enabled.
- *
- * Without this workaround, webpack would import the CSS of all themes, which
- * would defeat the purpose of hot-reloading since all themes would be compiled,
- * which would result in compilation times on the order of 30s, even on a
- * powerful machine.
- *
- * For more details, see webpack.config.js:184 (string-replace-loader)
- */
-if (process.env.NODE_ENV === "development") {
-    ("use theming");
-    /**
-     * Clean up old hot-module script injections as they hog up memory
-     * and anything other than the newest one is really not needed at all.
-     * We don't need to do it more frequently than every half a minute or so,
-     * but it's done to delay full page reload due to app slowness.
-     */
-    setInterval(() => {
-        const elements = Array.from(document.querySelectorAll("script[src*=hot-update]"));
-        if (elements.length > 1) {
-            const oldInjects = elements.slice(0, elements.length - 1);
-            oldInjects.forEach((e) => e.remove());
-        }
-    }, 1000);
-}
diff --git a/src/vector/index.ts b/src/vector/index.ts
index 1b9a59d099..04143ba36e 100644
--- a/src/vector/index.ts
+++ b/src/vector/index.ts
@@ -22,14 +22,6 @@ import "./modernizr";
 // in webpack.config.js
 require("katex/dist/katex.css");
 
-/**
- * This require is necessary only for purposes of CSS hot-reload, as otherwise
- * webpack has some incredible problems figuring out which CSS files should be
- * hot-reloaded, even with proper hints for the loader.
- *
- * On production build it's going to be an empty module, so don't worry about that.
- */
-require("./devcss");
 require("./localstorage-fix");
 
 async function settled(...promises: Array<Promise<any>>): Promise<void> {
diff --git a/webpack.config.js b/webpack.config.js
index 054dada461..ff0e8cfaeb 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -36,15 +36,6 @@ const cssThemes = {
     "theme-dark-custom": "./node_modules/matrix-react-sdk/res/themes/dark-custom/css/dark-custom.pcss",
 };
 
-function getActiveThemes() {
-    // Default to `light` theme when the MATRIX_THEMES environment variable is not defined.
-    const theme = process.env.MATRIX_THEMES ?? "light";
-    return theme
-        .split(",")
-        .map((x) => x.trim())
-        .filter(Boolean);
-}
-
 // See docs/customisations.md
 let fileOverrides = {
     /* {[file: string]: string} */
@@ -105,7 +96,6 @@ module.exports = (env, argv) => {
     //      (called to build nightly and develop.element.io)
     const nodeEnv = argv.mode;
     const devMode = nodeEnv !== "production";
-    const useHMR = process.env.CSS_HOT_RELOAD === "1" && devMode;
     const enableMinification = !devMode && !process.env.CI_PACKAGE;
 
     const development = {};
@@ -130,19 +120,6 @@ module.exports = (env, argv) => {
     const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), "..", "src");
     const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), "..", "src");
 
-    const ACTIVE_THEMES = getActiveThemes();
-    function getThemesImports() {
-        const imports = ACTIVE_THEMES.map((t) => {
-            return cssThemes[`theme-${t}`].replace("./node_modules/", ""); // theme import path
-        });
-        const s = JSON.stringify(ACTIVE_THEMES);
-        return `
-            window.MX_insertedThemeStylesCounter = 0;
-            window.MX_DEV_ACTIVE_THEMES = (${s});
-            ${imports.map((i) => `import("${i}")`).join("\n")};
-        `;
-    }
-
     return {
         ...development,
 
@@ -157,7 +134,7 @@ module.exports = (env, argv) => {
                 import: "./src/serviceworker/index.ts",
                 filename: "sw.js", // update WebPlatform if this changes
             },
-            ...(useHMR ? {} : cssThemes),
+            ...cssThemes,
         },
 
         optimization: {
@@ -276,14 +253,6 @@ module.exports = (env, argv) => {
                 /highlight\.js[\\/]lib[\\/]languages/,
             ],
             rules: [
-                useHMR && {
-                    test: /devcss\.ts$/,
-                    loader: "string-replace-loader",
-                    options: {
-                        search: '"use theming";',
-                        replace: getThemesImports(),
-                    },
-                },
                 {
                     test: /\.(ts|js)x?$/,
                     include: (f) => {
@@ -369,42 +338,7 @@ module.exports = (env, argv) => {
                 {
                     test: /\.pcss$/,
                     use: [
-                        /**
-                         * This code is hopeful that no .pcss outside of our themes will be directly imported in any
-                         * of the JS/TS files.
-                         * Should be MUCH better with webpack 5, but we're stuck to this solution for now.
-                         */
-                        useHMR
-                            ? {
-                                  loader: "style-loader",
-                                  /**
-                                   * If we refactor the `theme.js` in `matrix-react-sdk` a little bit,
-                                   * we could try using `lazyStyleTag` here to add and remove styles on demand,
-                                   * that would nicely resolve issues of race conditions for themes,
-                                   * at least for development purposes.
-                                   */
-                                  options: {
-                                      insert: function insertBeforeAt(element) {
-                                          const parent = document.querySelector("head");
-                                          // We're in iframe
-                                          if (!window.MX_DEV_ACTIVE_THEMES) {
-                                              parent.appendChild(element);
-                                              return;
-                                          }
-                                          // Properly disable all other instances of themes
-                                          element.disabled = true;
-                                          element.onload = () => {
-                                              element.disabled = true;
-                                          };
-                                          const theme =
-                                              window.MX_DEV_ACTIVE_THEMES[window.MX_insertedThemeStylesCounter];
-                                          element.setAttribute("data-mx-theme", theme);
-                                          window.MX_insertedThemeStylesCounter++;
-                                          parent.appendChild(element);
-                                      },
-                                  },
-                              }
-                            : MiniCssExtractPlugin.loader,
+                        MiniCssExtractPlugin.loader,
                         {
                             loader: "css-loader",
                             options: {
@@ -642,8 +576,8 @@ module.exports = (env, argv) => {
 
             // This exports our CSS using the splitChunks and loaders above.
             new MiniCssExtractPlugin({
-                filename: useHMR ? "bundles/[name].css" : "bundles/[fullhash]/[name].css",
-                chunkFilename: useHMR ? "bundles/[name].css" : "bundles/[fullhash]/[name].css",
+                filename: "bundles/[fullhash]/[name].css",
+                chunkFilename: "bundles/[fullhash]/[name].css",
                 ignoreOrder: false, // Enable to remove warnings about conflicting order
             }),
 
diff --git a/yarn.lock b/yarn.lock
index 55f63f6cc1..662cfd8719 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10810,14 +10810,6 @@ string-length@^4.0.1:
     char-regex "^1.0.2"
     strip-ansi "^6.0.0"
 
-string-replace-loader@3:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/string-replace-loader/-/string-replace-loader-3.1.0.tgz#11ac6ee76bab80316a86af358ab773193dd57a4f"
-  integrity sha512-5AOMUZeX5HE/ylKDnEa/KKBqvlnFmRZudSOjVJHxhoJg9QYTwl1rECx7SLR8BBH7tfxb4Rp7EM2XVfQFxIhsbQ==
-  dependencies:
-    loader-utils "^2.0.0"
-    schema-utils "^3.0.0"
-
 "string-width-cjs@npm:string-width@^4.2.0":
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -10975,11 +10967,6 @@ strip-json-comments@^3.1.1:
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
 
-style-loader@4:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-4.0.0.tgz#0ea96e468f43c69600011e0589cb05c44f3b17a5"
-  integrity sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==
-
 stylehacks@^7.0.2:
   version "7.0.2"
   resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-7.0.2.tgz#cc0ec317e9d5b30fdbdfe7ed6b8d3b1a8c57fa06"