diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67483da6300147aae40690fc8f132745cae179ba..d54930a1284aab33f1da2fd55eef1ad89112717a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,9 @@ Note that since we don't clearly distinguish between a public and private interf
 - Add ``allowTransparentBackfaces`` parameter to support double-sided rendering of transparent geometries
 - Fix handling of case insensitive mmCIF enumeration fields (including entity.type)
 - Fix ``disable-wboit`` Viewer GET param
+- Add support for React 18.
+  - Used by importing ``createPluginUI`` from ``mol-plugin-ui/react18``;
+  - In Mol* 4.0, React 18 will become the default option.
 
 ## [v3.5.0] - 2022-03-25
 
diff --git a/package-lock.json b/package-lock.json
index 8bb1b81fd431e4ef202db81b29795362fd0536a0..529818656e642eccfafae36084ffcd5458763cd1 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index 36fa245fe0f837c51420f164b6aee7d3879fe7f2..cf81fdeabec5df7c1fa0bff585c66be73152729b 100644
--- a/package.json
+++ b/package.json
@@ -101,6 +101,8 @@
     "@types/cors": "^2.8.12",
     "@types/gl": "^4.1.0",
     "@types/jest": "^27.4.1",
+    "@types/react": "^17.0.43",
+    "@types/react-dom": "^17.0.14",
     "@typescript-eslint/eslint-plugin": "^5.14.0",
     "@typescript-eslint/parser": "^5.14.0",
     "benchmark": "^2.1.4",
@@ -118,13 +120,15 @@
     "mini-css-extract-plugin": "^2.6.0",
     "path-browserify": "^1.0.1",
     "raw-loader": "^4.0.2",
+    "react": "^18.0.0",
+    "react-dom": "^18.0.0",
     "sass": "^1.49.9",
     "sass-loader": "^12.6.0",
     "simple-git": "^3.3.0",
     "stream-browserify": "^3.0.0",
     "style-loader": "^3.3.1",
     "ts-jest": "^27.1.3",
-    "typescript": "^4.6.2",
+    "typescript": "^4.6.3",
     "webpack": "^5.70.0",
     "webpack-cli": "^4.9.2"
   },
@@ -135,8 +139,6 @@
     "@types/express": "^4.17.13",
     "@types/node": "^16.11.26",
     "@types/node-fetch": "^2.6.1",
-    "@types/react": "^17.0.40",
-    "@types/react-dom": "^17.0.13",
     "@types/swagger-ui-dist": "3.30.1",
     "argparse": "^2.0.1",
     "body-parser": "^1.19.2",
@@ -154,7 +156,7 @@
     "xhr2": "^0.2.1"
   },
   "peerDependencies": {
-    "react": "^17.0.2 || ^16.14.0",
-    "react-dom": "^17.0.2 || ^16.14.0"
+    "react": "^18.0.0 || ^17.0.2 || ^16.14.0",
+    "react-dom": "^18.0.0 || ^17.0.2 || ^16.14.0"
   }
 }
diff --git a/src/apps/docking-viewer/index.ts b/src/apps/docking-viewer/index.ts
index 82e9745865cdf6211be9beb2d0e0170307c45f25..8242e62030159354074c3e99d9e3f95bea9b57a9 100644
--- a/src/apps/docking-viewer/index.ts
+++ b/src/apps/docking-viewer/index.ts
@@ -8,7 +8,7 @@
 import { Structure } from '../../mol-model/structure';
 import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
 import { PluginStateObject as PSO, PluginStateTransform } from '../../mol-plugin-state/objects';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { PluginLayoutControlsDisplay } from '../../mol-plugin/layout';
 import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
diff --git a/src/apps/viewer/app.ts b/src/apps/viewer/app.ts
index 30b586aec77d209dc57e8ca76493773713871870..eee9375c9e8903b1f99eb5743c6d6b1ddd71a25a 100644
--- a/src/apps/viewer/app.ts
+++ b/src/apps/viewer/app.ts
@@ -32,7 +32,7 @@ import { createVolumeRepresentationParams } from '../../mol-plugin-state/helpers
 import { PluginStateObject } from '../../mol-plugin-state/objects';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
 import { TrajectoryFromModelAndCoordinates } from '../../mol-plugin-state/transforms/model';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { DefaultPluginUISpec, PluginUISpec } from '../../mol-plugin-ui/spec';
 import { PluginCommands } from '../../mol-plugin/commands';
diff --git a/src/examples/alpha-orbitals/index.ts b/src/examples/alpha-orbitals/index.ts
index 1322a5b0d7ca06f015b7fa77b7860d4033447eb8..6b600e0bdeffa952b918cb7d872597e4769bcfd9 100644
--- a/src/examples/alpha-orbitals/index.ts
+++ b/src/examples/alpha-orbitals/index.ts
@@ -11,7 +11,7 @@ import { SphericalBasisOrder } from '../../extensions/alpha-orbitals/spherical-f
 import { BasisAndOrbitals, CreateOrbitalDensityVolume, CreateOrbitalRepresentation3D, CreateOrbitalVolume, StaticBasisAndOrbitals } from '../../extensions/alpha-orbitals/transforms';
 import { canComputeGrid3dOnGPU } from '../../mol-gl/compute/grid3d';
 import { PluginStateObject } from '../../mol-plugin-state/objects';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
 import { PluginCommands } from '../../mol-plugin/commands';
diff --git a/src/examples/basic-wrapper/index.ts b/src/examples/basic-wrapper/index.ts
index f63563caa93974bc7134ace60cddad9d2bcbdb98..27d660a292e6db8c7cae553912a88110bf6672f6 100644
--- a/src/examples/basic-wrapper/index.ts
+++ b/src/examples/basic-wrapper/index.ts
@@ -9,7 +9,7 @@ import { EmptyLoci } from '../../mol-model/loci';
 import { StructureSelection } from '../../mol-model/structure';
 import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/model-index';
 import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
 import { PluginCommands } from '../../mol-plugin/commands';
diff --git a/src/examples/lighting/index.ts b/src/examples/lighting/index.ts
index b81118dd72a74befa74870f5beb83f810d9efca9..23b254b5e6065f8df1fcf3fd9403db118020d01b 100644
--- a/src/examples/lighting/index.ts
+++ b/src/examples/lighting/index.ts
@@ -6,7 +6,7 @@
 
 import { Canvas3DProps } from '../../mol-canvas3d/canvas3d';
 import { BuiltInTrajectoryFormat } from '../../mol-plugin-state/formats/trajectory';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
 import { PluginCommands } from '../../mol-plugin/commands';
diff --git a/src/examples/proteopedia-wrapper/index.ts b/src/examples/proteopedia-wrapper/index.ts
index f762038f49628ad95e5cb636401f9ccb7087ac49..f371c943fa115226315873750f40596a10993c42 100644
--- a/src/examples/proteopedia-wrapper/index.ts
+++ b/src/examples/proteopedia-wrapper/index.ts
@@ -10,7 +10,7 @@ import { AnimateModelIndex } from '../../mol-plugin-state/animation/built-in/mod
 import { createStructureRepresentationParams } from '../../mol-plugin-state/helpers/structure-representation-params';
 import { PluginStateObject, PluginStateObject as PSO } from '../../mol-plugin-state/objects';
 import { StateTransforms } from '../../mol-plugin-state/transforms';
-import { createPluginUI } from '../../mol-plugin-ui';
+import { createPluginUI } from '../../mol-plugin-ui/react18';
 import { PluginUIContext } from '../../mol-plugin-ui/context';
 import { DefaultPluginUISpec } from '../../mol-plugin-ui/spec';
 import { CreateVolumeStreamingInfo, InitVolumeStreaming } from '../../mol-plugin/behavior/dynamic/volume-streaming/transformers';
diff --git a/src/mol-plugin-ui/react18.ts b/src/mol-plugin-ui/react18.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6309eebc1a7f0bd49aed99ac13662ac0a8a02bb4
--- /dev/null
+++ b/src/mol-plugin-ui/react18.ts
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2018-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author David Sehnal <david.sehnal@gmail.com>
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { createElement } from 'react';
+import { createRoot } from 'react-dom/client';
+import { Plugin } from './plugin';
+import { PluginUIContext } from './context';
+import { DefaultPluginUISpec, PluginUISpec } from './spec';
+
+export async function createPluginUI(target: HTMLElement, spec?: PluginUISpec, options?: { onBeforeUIRender?: (ctx: PluginUIContext) => (Promise<void> | void) }) {
+    const ctx = new PluginUIContext(spec || DefaultPluginUISpec());
+    await ctx.init();
+    if (options?.onBeforeUIRender) {
+        await options.onBeforeUIRender(ctx);
+    }
+    createRoot(target).render(createElement(Plugin, { plugin: ctx }));
+    return ctx;
+}
\ No newline at end of file