diff --git a/.changeset/five-icons-sneeze.md b/.changeset/five-icons-sneeze.md
new file mode 100644
index 00000000000..d35709d745f
--- /dev/null
+++ b/.changeset/five-icons-sneeze.md
@@ -0,0 +1,5 @@
+---
+"@itwin/appui-react": minor
+---
+
+Use `ToolUtilities.defineIcon` to define icons for `RotateViewTool` and `ViewToggleCameraTool` tools.
diff --git a/ui/appui-react/src/appui-react.ts b/ui/appui-react/src/appui-react.ts
index 0581df9b541..7810a0b7338 100644
--- a/ui/appui-react/src/appui-react.ts
+++ b/ui/appui-react/src/appui-react.ts
@@ -857,6 +857,7 @@ import { UiFramework } from "./appui-react/UiFramework.js";
import { DefaultToolSettingsProvider } from "./appui-react/toolsettings/DefaultToolSettingsProvider.js";
import { IModelViewportControl } from "./appui-react/content/IModelViewport.js";
import { registerEditors } from "./appui-react/editors/registerEditors.js";
+import { defineToolIcons } from "./appui-react/tools/defineToolIcons.js";
UiFramework.controls.register(
"DefaultToolSettings",
@@ -865,6 +866,7 @@ UiFramework.controls.register(
UiFramework.controls.register(IModelViewportControl.id, IModelViewportControl);
registerEditors();
+defineToolIcons();
// #endregion "SideEffects"
diff --git a/ui/appui-react/src/appui-react/tools/CoreToolDefinitions.tsx b/ui/appui-react/src/appui-react/tools/CoreToolDefinitions.tsx
index 9b5a69fda5d..f8233299163 100644
--- a/ui/appui-react/src/appui-react/tools/CoreToolDefinitions.tsx
+++ b/ui/appui-react/src/appui-react/tools/CoreToolDefinitions.tsx
@@ -35,10 +35,6 @@ import {
ConditionalStringValue,
} from "@itwin/appui-abstract";
import { ToolUtilities } from "@itwin/imodel-components-react";
-import {
- SvgCameraAnimation,
- SvgCameraAnimationDisabled,
-} from "@itwin/itwinui-icons-react";
import { getIsHiddenIfSelectionNotActive } from "../selection/SelectionContextItemDef.js";
import { CommandItemDef } from "../shared/CommandItemDef.js";
import { ToolItemDef } from "../shared/ToolItemDef.js";
@@ -46,15 +42,9 @@ import { SyncUiEventId } from "../syncui/UiSyncEvent.js";
import { GroupItemDef } from "../toolbar/GroupItem.js";
import { RestoreFrontstageLayoutTool } from "./RestoreLayoutTool.js";
import { UiFramework } from "../UiFramework.js";
-import { SvgGyroscope } from "../icons/SvgGyroscope.js";
import { SvgSectionTool } from "../icons/SvgSectionTool.js";
import { SvgSelectionClear } from "../icons/SvgSelectionClear.js";
-import {
- SvgMeasure,
- SvgProcess,
- SvgRotateLeft,
-} from "@itwin/itwinui-icons-react";
-import { ConditionalIconItem } from "@itwin/core-react";
+import { SvgMeasure, SvgProcess } from "@itwin/itwinui-icons-react";
import type { ToolbarItems } from "./ToolbarItems.js";
import { getActiveViewport } from "../utils/getActiveViewport.js";
import type { ToolItemProps } from "../shared/ItemProps.js";
@@ -122,15 +112,6 @@ export class CoreTools {
public static get rotateViewCommand() {
return createForTool(RotateViewTool, {
- icon: new ConditionalIconItem(() => {
- const viewport = getActiveViewport();
- if (viewport?.view.is2d()) return ;
- return ;
- }, [
- SyncUiEventId.ActiveContentChanged,
- SyncUiEventId.ActiveViewportChanged,
- SyncUiEventId.ViewStateChanged,
- ]),
execute: async () =>
IModelApp.tools.run(
RotateViewTool.toolId,
@@ -167,18 +148,6 @@ export class CoreTools {
public static get toggleCameraViewCommand() {
return createForTool(ViewToggleCameraTool, {
- iconSpec: new ConditionalIconItem(() => {
- const viewport = getActiveViewport();
- if (viewport?.view.is3d() && viewport?.isCameraOn) {
- return ;
- }
-
- return ;
- }, [
- SyncUiEventId.ActiveContentChanged,
- SyncUiEventId.ActiveViewportChanged,
- SyncUiEventId.ViewStateChanged,
- ]),
label: new ConditionalStringValue(() => {
const viewport = getActiveViewport();
if (viewport?.view.is3d() && viewport?.isCameraOn) {
diff --git a/ui/appui-react/src/appui-react/tools/defineToolIcons.tsx b/ui/appui-react/src/appui-react/tools/defineToolIcons.tsx
new file mode 100644
index 00000000000..30254f14162
--- /dev/null
+++ b/ui/appui-react/src/appui-react/tools/defineToolIcons.tsx
@@ -0,0 +1,60 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
+ * See LICENSE.md in the project root for license terms and full copyright notice.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as React from "react";
+import { RotateViewTool, ViewToggleCameraTool } from "@itwin/core-frontend";
+import { ToolUtilities } from "@itwin/imodel-components-react";
+import {
+ SvgCameraAnimation,
+ SvgCameraAnimationDisabled,
+ SvgRotateLeft,
+} from "@itwin/itwinui-icons-react";
+import { useConditionalValue } from "../hooks/useConditionalValue.js";
+import { getActiveViewport } from "../utils/getActiveViewport.js";
+import { SyncUiEventId } from "../syncui/UiSyncEvent.js";
+import { SvgGyroscope } from "../icons/SvgGyroscope.js";
+
+import type { ToolType } from "@itwin/core-frontend";
+
+/** @internal */
+export function defineToolIcons() {
+ defineIcon(RotateViewTool, );
+ defineIcon(ViewToggleCameraTool, );
+}
+
+function defineIcon(toolType: ToolType, icon: React.ReactElement) {
+ if (ToolUtilities.isWithIcon(toolType)) return;
+ ToolUtilities.defineIcon(toolType, icon);
+}
+
+function RotateViewIcon() {
+ const is2d = useConditionalValue(() => {
+ const viewport = getActiveViewport();
+ return viewport?.view.is2d() ?? false;
+ }, [
+ SyncUiEventId.ActiveContentChanged,
+ SyncUiEventId.ActiveViewportChanged,
+ SyncUiEventId.ViewStateChanged,
+ ]);
+
+ return is2d ? : ;
+}
+
+function ToggleCameraViewIcon() {
+ const cameraEnabled = useConditionalValue(() => {
+ const viewport = getActiveViewport();
+ return viewport?.view.is3d() && viewport?.isCameraOn;
+ }, [
+ SyncUiEventId.ActiveContentChanged,
+ SyncUiEventId.ActiveViewportChanged,
+ SyncUiEventId.ViewStateChanged,
+ ]);
+
+ return cameraEnabled ? (
+
+ ) : (
+
+ );
+}