diff --git a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
index 6c6b44a63..bd9a8b5b8 100644
--- a/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
+++ b/arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts
@@ -356,6 +356,7 @@ import { Account } from './contributions/account';
 import { SidebarBottomMenuWidget } from './theia/core/sidebar-bottom-menu-widget';
 import { SidebarBottomMenuWidget as TheiaSidebarBottomMenuWidget } from '@theia/core/lib/browser/shell/sidebar-bottom-menu-widget';
 import { CreateCloudCopy } from './contributions/create-cloud-copy';
+import { NativeImageCache } from './native-image-cache';
 
 export default new ContainerModule((bind, unbind, isBound, rebind) => {
   // Commands and toolbar items
@@ -1034,4 +1035,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
   bind(FrontendApplicationContribution).toService(DaemonPort);
   bind(IsOnline).toSelf().inSingletonScope();
   bind(FrontendApplicationContribution).toService(IsOnline);
+  // manages native images for the electron menu icons
+  bind(NativeImageCache).toSelf().inSingletonScope();
+  bind(FrontendApplicationContribution).toService(NativeImageCache);
 });
diff --git a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts
index a14d6a541..dfc02b7a4 100644
--- a/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts
+++ b/arduino-ide-extension/src/browser/contributions/open-recent-sketch.ts
@@ -1,55 +1,65 @@
-import { inject, injectable } from '@theia/core/shared/inversify';
-import { WorkspaceServer } from '@theia/workspace/lib/common/workspace-protocol';
+import { NativeImage } from '@theia/core/electron-shared/electron';
+import { ThemeService } from '@theia/core/lib/browser/theming';
 import {
   Disposable,
   DisposableCollection,
 } from '@theia/core/lib/common/disposable';
-import {
-  SketchContribution,
-  CommandRegistry,
-  MenuModelRegistry,
-  Sketch,
-} from './contribution';
+import { MenuAction } from '@theia/core/lib/common/menu';
+import { nls } from '@theia/core/lib/common/nls';
+import { inject, injectable } from '@theia/core/shared/inversify';
+import { SketchesError } from '../../common/protocol';
+import { ConfigServiceClient } from '../config/config-service-client';
 import { ArduinoMenus } from '../menu/arduino-menus';
-import { MainMenuManager } from '../../common/main-menu-manager';
-import { OpenSketch } from './open-sketch';
+import {
+  isThemeNativeImage,
+  NativeImageCache,
+  ThemeNativeImage,
+} from '../native-image-cache';
 import { NotificationCenter } from '../notification-center';
-import { nls } from '@theia/core/lib/common';
-import { SketchesError } from '../../common/protocol';
+import { CloudSketchContribution } from './cloud-contribution';
+import { CommandRegistry, MenuModelRegistry, Sketch } from './contribution';
+import { OpenSketch } from './open-sketch';
 
 @injectable()
-export class OpenRecentSketch extends SketchContribution {
+export class OpenRecentSketch extends CloudSketchContribution {
   @inject(CommandRegistry)
-  protected readonly commandRegistry: CommandRegistry;
-
+  private readonly commandRegistry: CommandRegistry;
   @inject(MenuModelRegistry)
-  protected readonly menuRegistry: MenuModelRegistry;
-
-  @inject(MainMenuManager)
-  protected readonly mainMenuManager: MainMenuManager;
-
-  @inject(WorkspaceServer)
-  protected readonly workspaceServer: WorkspaceServer;
-
+  private readonly menuRegistry: MenuModelRegistry;
   @inject(NotificationCenter)
-  protected readonly notificationCenter: NotificationCenter;
+  private readonly notificationCenter: NotificationCenter;
+  @inject(NativeImageCache)
+  private readonly imageCache: NativeImageCache;
+  @inject(ConfigServiceClient)
+  private readonly configServiceClient: ConfigServiceClient;
+  @inject(ThemeService)
+  private readonly themeService: ThemeService;
 
-  protected toDispose = new DisposableCollection();
+  private readonly toDisposeBeforeRegister = new DisposableCollection();
+  private readonly toDispose = new DisposableCollection(
+    this.toDisposeBeforeRegister
+  );
+  private cloudImage: NativeImage | ThemeNativeImage;
 
   override onStart(): void {
-    this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
-      this.refreshMenu(sketches)
-    );
+    this.toDispose.pushAll([
+      this.notificationCenter.onRecentSketchesDidChange(({ sketches }) =>
+        this.refreshMenu(sketches)
+      ),
+      this.themeService.onDidColorThemeChange(() => this.update()),
+    ]);
   }
 
-  override async onReady(): Promise<void> {
-    this.update();
+  onStop(): void {
+    this.toDispose.dispose();
   }
 
-  private update(forceUpdate?: boolean): void {
-    this.sketchesService
-      .recentlyOpenedSketches(forceUpdate)
-      .then((sketches) => this.refreshMenu(sketches));
+  override async onReady(): Promise<void> {
+    this.update();
+    this.imageCache.getImage('cloud').then((image) => {
+      this.cloudImage = image;
+      this.update();
+    });
   }
 
   override registerMenus(registry: MenuModelRegistry): void {
@@ -60,14 +70,20 @@ export class OpenRecentSketch extends SketchContribution {
     );
   }
 
+  private update(forceUpdate?: boolean): void {
+    this.sketchesService
+      .recentlyOpenedSketches(forceUpdate)
+      .then((sketches) => this.refreshMenu(sketches));
+  }
+
   private refreshMenu(sketches: Sketch[]): void {
     this.register(sketches);
-    this.mainMenuManager.update();
+    this.menuManager.update();
   }
 
-  protected register(sketches: Sketch[]): void {
+  private register(sketches: Sketch[]): void {
     const order = 0;
-    this.toDispose.dispose();
+    this.toDisposeBeforeRegister.dispose();
     for (const sketch of sketches) {
       const { uri } = sketch;
       const command = { id: `arduino-open-recent--${uri}` };
@@ -88,15 +104,16 @@ export class OpenRecentSketch extends SketchContribution {
         },
       };
       this.commandRegistry.registerCommand(command, handler);
+      const menuAction = this.assignImage(sketch, {
+        commandId: command.id,
+        label: sketch.name,
+        order: String(order),
+      });
       this.menuRegistry.registerMenuAction(
         ArduinoMenus.FILE__OPEN_RECENT_SUBMENU,
-        {
-          commandId: command.id,
-          label: sketch.name,
-          order: String(order),
-        }
+        menuAction
       );
-      this.toDispose.pushAll([
+      this.toDisposeBeforeRegister.pushAll([
         new DisposableCollection(
           Disposable.create(() =>
             this.commandRegistry.unregisterCommand(command)
@@ -108,4 +125,25 @@ export class OpenRecentSketch extends SketchContribution {
       ]);
     }
   }
+
+  private assignImage(sketch: Sketch, menuAction: MenuAction): MenuAction {
+    const image = this.nativeImageForTheme();
+    if (image) {
+      const dataDirUri = this.configServiceClient.tryGetDataDirUri();
+      const isCloud = this.createFeatures.isCloud(sketch, dataDirUri);
+      if (isCloud) {
+        Object.assign(menuAction, { nativeImage: image });
+      }
+    }
+    return menuAction;
+  }
+
+  private nativeImageForTheme(): NativeImage | undefined {
+    const image = this.cloudImage;
+    if (isThemeNativeImage(image)) {
+      const themeType = this.themeService.getCurrentTheme().type;
+      return themeType === 'light' ? image.light : image.dark;
+    }
+    return image;
+  }
 }
diff --git a/arduino-ide-extension/src/browser/native-image-cache.ts b/arduino-ide-extension/src/browser/native-image-cache.ts
new file mode 100644
index 000000000..13948fee6
--- /dev/null
+++ b/arduino-ide-extension/src/browser/native-image-cache.ts
@@ -0,0 +1,116 @@
+import {
+  NativeImage,
+  nativeImage,
+  Size,
+} from '@theia/core/electron-shared/electron';
+import { Endpoint } from '@theia/core/lib/browser/endpoint';
+import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
+import { Deferred } from '@theia/core/lib/common/promise-util';
+import { injectable } from '@theia/core/shared/inversify';
+import fetch from 'cross-fetch';
+
+const nativeImageIdentifierLiterals = ['cloud'] as const;
+export type NativeImageIdentifier =
+  typeof nativeImageIdentifierLiterals[number];
+export const nativeImages: Record<
+  NativeImageIdentifier,
+  string | { light: string; dark: string }
+> = {
+  cloud: { light: 'cloud-light.png', dark: 'cloud-dark.png' },
+};
+
+export interface ThemeNativeImage {
+  readonly light: NativeImage;
+  readonly dark: NativeImage;
+}
+
+export function isThemeNativeImage(arg: unknown): arg is ThemeNativeImage {
+  return (
+    typeof arg === 'object' &&
+    (<ThemeNativeImage>arg).light !== undefined &&
+    (<ThemeNativeImage>arg).dark !== undefined
+  );
+}
+
+type Image = NativeImage | ThemeNativeImage;
+
+@injectable()
+export class NativeImageCache implements FrontendApplicationContribution {
+  private readonly cache = new Map<NativeImageIdentifier, Image>();
+  private readonly loading = new Map<NativeImageIdentifier, Promise<Image>>();
+
+  onStart(): void {
+    Object.keys(nativeImages).forEach((identifier: NativeImageIdentifier) =>
+      this.getImage(identifier)
+    );
+  }
+
+  tryGetImage(identifier: NativeImageIdentifier): Image | undefined {
+    return this.cache.get(identifier);
+  }
+
+  async getImage(identifier: NativeImageIdentifier): Promise<Image> {
+    const image = this.cache.get(identifier);
+    if (image) {
+      return image;
+    }
+    let loading = this.loading.get(identifier);
+    if (!loading) {
+      const deferred = new Deferred<Image>();
+      loading = deferred.promise;
+      this.loading.set(identifier, loading);
+      this.fetchImage(identifier).then(
+        (image) => {
+          if (!this.cache.has(identifier)) {
+            this.cache.set(identifier, image);
+          }
+          this.loading.delete(identifier);
+          deferred.resolve(image);
+        },
+        (err) => {
+          this.loading.delete(identifier);
+          deferred.reject(err);
+        }
+      );
+    }
+    return loading;
+  }
+
+  private async fetchImage(identifier: NativeImageIdentifier): Promise<Image> {
+    const value = nativeImages[identifier];
+    if (typeof value === 'string') {
+      return this.fetchIconData(value);
+    }
+    const [light, dark] = await Promise.all([
+      this.fetchIconData(value.light),
+      this.fetchIconData(value.dark),
+    ]);
+    return { light, dark };
+  }
+
+  private async fetchIconData(filename: string): Promise<NativeImage> {
+    const path = `nativeImage/${filename}`;
+    const endpoint = new Endpoint({ path }).getRestUrl().toString();
+    const response = await fetch(endpoint);
+    const arrayBuffer = await response.arrayBuffer();
+    const view = new Uint8Array(arrayBuffer);
+    const buffer = Buffer.alloc(arrayBuffer.byteLength);
+    buffer.forEach((_, index) => (buffer[index] = view[index]));
+    const image = nativeImage.createFromBuffer(buffer);
+    return this.maybeResize(image);
+  }
+
+  private maybeResize(image: NativeImage): NativeImage {
+    const currentSize = image.getSize();
+    if (sizeEquals(currentSize, preferredSize)) {
+      return image;
+    }
+    return image.resize(preferredSize);
+  }
+}
+
+const pixel = 16;
+const preferredSize: Size = { height: pixel, width: pixel };
+function sizeEquals(left: Size, right: Size): boolean {
+  return left.height === right.height && left.width === right.width;
+}
diff --git a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts
index bcb313a6f..4a2f2faf4 100644
--- a/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts
+++ b/arduino-ide-extension/src/electron-browser/theia/core/electron-main-menu-factory.ts
@@ -1,7 +1,9 @@
 import * as remote from '@theia/core/electron-shared/@electron/remote';
+import { NativeImage } from '@theia/core/electron-shared/electron';
 import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider';
 import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
 import {
+  ActionMenuNode,
   CommandMenuNode,
   CompoundMenuNode,
   CompoundMenuNodeRole,
@@ -278,6 +280,12 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
           delete menuItem.click;
         }
       }
+
+      // Native image customization for IDE2
+      if (isMenuNodeWithNativeImage(node)) {
+        menuItem.icon = node.action.nativeImage;
+      }
+
       parentItems.push(menuItem);
 
       if (this.commandRegistry.getToggledHandler(commandId, ...args)) {
@@ -314,3 +322,23 @@ const AlwaysVisibleSubmenus: MenuPath[] = [
   ArduinoMenus.TOOLS__PORTS_SUBMENU, // #655
   ArduinoMenus.FILE__SKETCHBOOK_SUBMENU, // #569
 ];
+
+// Theia does not support icons for electron menu items.
+// This is a hack to show a cloud icon as a native image for the cloud sketches in `File` > `Open Recent` menu.
+type MenuNodeWithNativeImage = MenuNode & {
+  action: ActionMenuNode & { nativeImage: NativeImage };
+};
+type ActionMenuNodeWithNativeImage = ActionMenuNode & {
+  nativeImage: NativeImage;
+};
+function isMenuNodeWithNativeImage(
+  node: MenuNode
+): node is MenuNodeWithNativeImage {
+  if (node instanceof ActionMenuNode) {
+    const action: unknown = node['action'];
+    if ((<ActionMenuNodeWithNativeImage>action).nativeImage !== undefined) {
+      return true;
+    }
+  }
+  return false;
+}
diff --git a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts
index 5cd81f7c3..349faf482 100644
--- a/arduino-ide-extension/src/node/arduino-ide-backend-module.ts
+++ b/arduino-ide-extension/src/node/arduino-ide-backend-module.ts
@@ -119,6 +119,7 @@ import {
   PluginDeployer_GH_12064,
 } from './theia/plugin-ext/plugin-deployer';
 import { SettingsReader } from './settings-reader';
+import { NativeImageDataProvider } from './native-image-data-provider';
 
 export default new ContainerModule((bind, unbind, isBound, rebind) => {
   bind(BackendApplication).toSelf().inSingletonScope();
@@ -406,6 +407,9 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
   rebind(PluginDeployer).to(PluginDeployer_GH_12064).inSingletonScope();
 
   bind(SettingsReader).toSelf().inSingletonScope();
+  // to serve native images for the electron menus
+  bind(NativeImageDataProvider).toSelf().inSingletonScope();
+  bind(BackendApplicationContribution).toService(NativeImageDataProvider);
 });
 
 function bindChildLogger(bind: interfaces.Bind, name: string): void {
diff --git a/arduino-ide-extension/src/node/native-image-data-provider.ts b/arduino-ide-extension/src/node/native-image-data-provider.ts
new file mode 100644
index 000000000..beb7b265f
--- /dev/null
+++ b/arduino-ide-extension/src/node/native-image-data-provider.ts
@@ -0,0 +1,61 @@
+import { Deferred } from '@theia/core/lib/common/promise-util';
+import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
+import { Application } from '@theia/core/shared/express';
+import { injectable } from '@theia/core/shared/inversify';
+import { promises as fs } from 'fs';
+import { join } from 'path';
+import { ErrnoException } from './utils/errors';
+
+@injectable()
+export class NativeImageDataProvider implements BackendApplicationContribution {
+  private readonly rootPath = join(__dirname, '../../src/node/static/icons');
+  private readonly dataCache = new Map<string, Promise<Buffer | undefined>>();
+
+  onStart(): void {
+    console.log(`Serving native images from ${this.rootPath}`);
+  }
+
+  configure(app: Application): void {
+    app.get('/nativeImage/:filename', async (req, resp) => {
+      const filename = req.params.filename;
+      if (!filename) {
+        resp.status(400).send('Bad Request');
+        return;
+      }
+      try {
+        const data = await this.getOrCreateData(filename);
+        if (!data) {
+          resp.status(404).send('Not found');
+          return;
+        }
+        resp.send(data);
+      } catch (err) {
+        resp.status(500).send(err instanceof Error ? err.message : String(err));
+      }
+    });
+  }
+
+  private async getOrCreateData(filename: string): Promise<Buffer | undefined> {
+    let data = this.dataCache.get(filename);
+    if (!data) {
+      const deferred = new Deferred<Buffer | undefined>();
+      data = deferred.promise;
+      this.dataCache.set(filename, data);
+      const path = join(this.rootPath, filename);
+      fs.readFile(path).then(
+        (buffer) => deferred.resolve(buffer),
+        (err) => {
+          if (ErrnoException.isENOENT(err)) {
+            console.error(`File not found: ${path}`);
+            deferred.resolve(undefined);
+          } else {
+            console.error(`Failed to load file: ${path}`, err);
+            this.dataCache.delete(filename);
+            deferred.reject(err);
+          }
+        }
+      );
+    }
+    return data;
+  }
+}
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark.png b/arduino-ide-extension/src/node/static/icons/cloud-dark.png
new file mode 100644
index 000000000..464646dd0
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark.png differ
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png b/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png
new file mode 100644
index 000000000..14af1ce5e
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark@2x.png differ
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png b/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png
new file mode 100644
index 000000000..4d857c1d5
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-dark@3x.png differ
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light.png b/arduino-ide-extension/src/node/static/icons/cloud-light.png
new file mode 100644
index 000000000..51bf2be84
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light.png differ
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png b/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png
new file mode 100644
index 000000000..d42259fb1
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light@2x.png differ
diff --git a/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png b/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png
new file mode 100644
index 000000000..777688e04
Binary files /dev/null and b/arduino-ide-extension/src/node/static/icons/cloud-light@3x.png differ