Window Zoom Controls #1024

Open
opened 2026-01-29 16:55:32 +00:00 by claunia · 18 comments
Owner

Originally created by @AeonSake on GitHub (Dec 2, 2025).

Is there currently any way to programmatically set/get the zoom level/factor of a window? I believe the electron side exposes a zoom API on the webFrame, but the BrowserWindow and WebContents classes currently don't provide any possibility to use it.

I understand that this might not be a very urgent feature, but it would be great to have nonetheless. Likewise, I would also gladly accept any alternative solution to get this to work.

Originally created by @AeonSake on GitHub (Dec 2, 2025). Is there currently any way to programmatically set/get the zoom level/factor of a window? I believe the electron side exposes a [zoom API](https://www.electronjs.org/docs/latest/api/web-frame#webframesetzoomfactorfactor) on the `webFrame`, but the `BrowserWindow` and `WebContents` classes currently don't provide any possibility to use it. I understand that this might not be a very urgent feature, but it would be great to have nonetheless. Likewise, I would also gladly accept any alternative solution to get this to work.
claunia added the help wantedFeatureapi labels 2026-01-29 16:55:32 +00:00
Author
Owner

@agracio commented on GitHub (Dec 2, 2025):

Is there any way to check the minimum supported version of this API or any Electron API for that matter?

@agracio commented on GitHub (Dec 2, 2025): Is there any way to check the minimum supported version of this API or any Electron API for that matter?
Author
Owner

@AeonSake commented on GitHub (Dec 2, 2025):

Good question; I don't think so? At least not directly. I would need to check the git history, but the zoom API has been part of electron since basically forever.

@AeonSake commented on GitHub (Dec 2, 2025): Good question; I don't think so? At least not directly. I would need to check the git history, but the zoom API has been part of electron since basically forever.
Author
Owner

@AeonSake commented on GitHub (Dec 2, 2025):

Checking the electron GitHub history, it looks like the zoom API has been part of the shell since the start. The oldest entry I could find was with the creation of the initial documentation more than 11 years ago (webFrame was called webView back then).

@AeonSake commented on GitHub (Dec 2, 2025): Checking the electron GitHub history, it looks like the zoom API has been part of the shell since the start. The oldest entry I could find was with the creation of the [initial documentation](https://github.com/electron/electron/commit/0bc8251e1b019cf018e2ffb4d6157db072e25e5f) more than 11 years ago (`webFrame` was called `webView` back then).
Author
Owner

@agracio commented on GitHub (Dec 2, 2025):

I can take a look at it later unless someone already picked that up.
Are there any other known APIs that are either needed or nice to have?

@agracio commented on GitHub (Dec 2, 2025): I can take a look at it later unless someone already picked that up. Are there any other known APIs that are either needed or nice to have?
Author
Owner

@AeonSake commented on GitHub (Dec 2, 2025):

Personally, I can't say I'm missing any other APIs beside the zoom functions, but maybe the clearCache and insertText functions could also be useful to some? There are also some functions for spell-checker stuff on the webFrame class.

There are also a lot of other functions in the webContents class that I feel like might be more interesting; especially the undo/redo/copy/cut/etc. functions and the navigation functions.

@AeonSake commented on GitHub (Dec 2, 2025): Personally, I can't say I'm missing any other APIs beside the zoom functions, but maybe the `clearCache` and `insertText` functions could also be useful to some? There are also some functions for spell-checker stuff on the `webFrame` class. There are also a lot of other functions in the [`webContents`](https://www.electronjs.org/docs/latest/api/web-contents) class that I feel like might be more interesting; especially the `undo`/`redo`/`copy`/`cut`/etc. functions and the navigation functions.
Author
Owner

@agracio commented on GitHub (Dec 2, 2025):

Would be great to have more APIs to finish at the same time, I will check for a few more and will get a PR ready in a day or two at most.
If you can compile a list of some APIs with links that would be a great start, cannot promise that all will be done but will start with some low hanging fruits first.

But undo/redo/cut/copy/paste might be complicated to implement.

@agracio commented on GitHub (Dec 2, 2025): Would be great to have more APIs to finish at the same time, I will check for a few more and will get a PR ready in a day or two at most. If you can compile a list of some APIs with links that would be a great start, cannot promise that all will be done but will start with some low hanging fruits first. But `undo/redo/cut/copy/paste` might be complicated to implement.
Author
Owner

@AeonSake commented on GitHub (Dec 2, 2025):

Sure, I can compile you a list of all (relevant) functions with links. Priority is up to you, though, as I personally care mainly for the zoom API. Regarding the clipboard functions, as far as I understood it, those are just the browser-control calls which should then correctly map back to the clipboard API. Most functions on the webContents class seem to just provide an interface to native browser controls and content.

@AeonSake commented on GitHub (Dec 2, 2025): Sure, I can compile you a list of all (relevant) functions with links. Priority is up to you, though, as I personally care mainly for the zoom API. Regarding the clipboard functions, as far as I understood it, those are just the browser-control calls which should then correctly map back to the clipboard API. Most functions on the `webContents` class _seem_ to just provide an interface to native browser controls and content.
Author
Owner

@agracio commented on GitHub (Dec 4, 2025):

After looking closely at the code it appears we do not have webFrame class at all.
Will take longer to implement since it would require creation of a new class on both sides.

@agracio commented on GitHub (Dec 4, 2025): After looking closely at the code it appears we do not have `webFrame` class at all. Will take longer to implement since it would require creation of a new class on both sides.
Author
Owner

@AeonSake commented on GitHub (Dec 4, 2025):

Loading Content

Zoom

API for window zoom factor and limits. Changes the underlying rendering zoom, not the CSS styling zoom. Zoom factor of 1.0 = 100% (default). All of these methods are also available on the webFrame instance and I'm not entirely sure, how these interact with each other.

User Agent

Might be useful for some applications/websites that rely on user agent fingerprinting.

Dynamic CSS

Allows to manipulate CSS at runtime. All of these methods are also available on the webFrame instance.

Audio

Global audio/mute controls for the entire window.

Edit / Select

Access to the (usually browser) controls for the edit history and text selection. All copy/paste operations are executed with the clipboard API.

Dev Tools

Additional methods to control dev tools.

Cache Control

Resource Monitoring

@AeonSake commented on GitHub (Dec 4, 2025): ### Loading Content - [`webContents.loadFile(filePath: string, options?: {query?: Record<string, string>, search?: string, hash?: string}) : Promise<void>`](https://www.electronjs.org/docs/latest/api/web-contents#contentsloadfilefilepath-options) - [`webContents.isLoading() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsisloading) - [`webContents.isLoadingMainFrame() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsisloadingmainframe) - [`webContents.isWaitingForResponse() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsiswaitingforresponse) - [`webContents.reload() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsreload) - [`webContents.reloadIgnoringCache() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsreloadignoringcache) - [`webContents.stop() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsstop) ### Zoom API for window zoom factor and limits. Changes the underlying rendering zoom, not the CSS styling zoom. Zoom factor of 1.0 = 100% (default). **All of these methods are also available on the `webFrame` instance and I'm not entirely sure, how these interact with each other.** - [`webContents.setZoomFactor(factor: double) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentssetzoomfactorfactor) - Factor must be greater than 0 - [`webContents.getZoomFactor() : double`](https://www.electronjs.org/docs/latest/api/web-contents#contentsgetzoomfactor) - [`webContents.setZoomLevel(level: number) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentssetzoomlevellevel) - Zoom _level_ and _factor_ do the same thing but work slightly differently. The `level` starts at 0 and each increment will increase/decrease the overall zoom by 20%. So a zoom level of 2 would be a total zoom of 140%, -1 would be 80%, etc. - [`webContents.getZoomLevel() : number`](https://www.electronjs.org/docs/latest/api/web-contents#contentsgetzoomlevel) - [`webContents.setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number) : Promise<void>`](https://www.electronjs.org/docs/latest/api/web-contents#contentssetvisualzoomlevellimitsminimumlevel-maximumlevel) - [`webContents.zoomFactor : double {get,set}`](https://www.electronjs.org/docs/latest/api/web-contents#contentszoomfactor) - [`webContents.zoomLevel : number {get,set}`](https://www.electronjs.org/docs/latest/api/web-contents#contentszoomlevel) ### User Agent Might be useful for some applications/websites that rely on user agent fingerprinting. - [`webContents.setUserAgent(userAgent: string) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentssetuseragentuseragent) - [`webContents.getUserAgent() : string`](https://www.electronjs.org/docs/latest/api/web-contents#contentsgetuseragent) - [`webContents.userAgent : string {get,set}`](https://www.electronjs.org/docs/latest/api/web-contents#contentsuseragent) ### Dynamic CSS Allows to manipulate CSS at runtime. **All of these methods are also available on the `webFrame` instance.** - [`webContents.insertCSS(css: string, options?: {cssOrigin?: string}) : Promise<string>`](https://www.electronjs.org/docs/latest/api/web-contents#contentsinsertcsscss-options) - The returned value represents a unique key that can be used with `removeInsertedCSS()` - [`webContents.removeInsertedCSS(key: string) : Promise<void>`](https://www.electronjs.org/docs/latest/api/web-contents#contentsremoveinsertedcsskey) ### Audio Global audio/mute controls for the entire window. - [`webContents.setAudioMuted(muted: boolean) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentssetaudiomutedmuted) - [`webContents.isAudioMuted() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsisaudiomuted) - [`webContents.isCurrentlyAudible() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsiscurrentlyaudible) - [`webContents.audioMuted : boolean {get,set}`](https://www.electronjs.org/docs/latest/api/web-contents#contentsaudiomuted) ### Edit / Select Access to the (usually browser) controls for the edit history and text selection. All copy/paste operations are executed with the clipboard API. - [`webContents.undo() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsundo) - [`webContents.redo() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsredo) - [`webContents.cut() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentscut) - [`webContents.copy() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentscopy) - [`webContents.copyImageAt(x: int, y: int) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentscopyimageatx-y) - [`webContents.paste() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentspaste) - [`webContents.pasteAndMatchStyle() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentspasteandmatchstyle) - [`webContents.insertText(text: string) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsinserttexttext) - [`webContents.replace(text: string) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsreplacetext) - [`webContents.replaceMisspelling(text: string) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsreplacemisspellingtext) - [`webContents.selectAll() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsselectall) - [`webContents.unselect() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsunselect) - [`webContents.adjustSelection(options: {start?: number, end?: number}) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsadjustselectionoptions) - [`webContents.centerSelection() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentscenterselection) - [`webContents.delete() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsdelete) - [`webContents.findInPage(text: string, options?: {forward?: boolean, findNext?: boolean, matchCase?: boolean}) : int`](https://www.electronjs.org/docs/latest/api/web-contents#contentsfindinpagetext-options) - The returned value is the ID of the request which is reported in the `found-in-page` event. - [`webContents.stopFindInPage(action: string) : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsstopfindinpageaction) - `action` must be one of the following: `clearSelection`, `keepSelection`, `activateSelection` ### Dev Tools Additional methods to control dev tools. - [`webContents.toggleDevTools() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentstoggledevtools) - [`webContents.closeDevTools() : void`](https://www.electronjs.org/docs/latest/api/web-contents#contentsclosedevtools) - [`webContents.isDevToolsOpened() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsisdevtoolsopened) - [`webContents.isDevToolsFocused() : boolean`](https://www.electronjs.org/docs/latest/api/web-contents#contentsisdevtoolsfocused) ### Cache Control - [`webFrame.clearCache() : void`](https://www.electronjs.org/docs/latest/api/web-frame#webframeclearcache) ### Resource Monitoring - [`webFrame.getResourceUsage() : {images: {count: number, size: number, liveSize: number}, scripts: {count: number, size: number, liveSize: number}, cssStyleSheets: {count: number, size: number, liveSize: number}, xslStyleSheets: {count: number, size: number, liveSize: number}, fonts: {count: number, size: number, liveSize: number}, other: {count: number, size: number, liveSize: number}}`](https://www.electronjs.org/docs/latest/api/web-frame#webframegetresourceusage)
Author
Owner

@AeonSake commented on GitHub (Dec 4, 2025):

After looking closely at the code it appears we do not have webFrame class at all. Will take longer to implement since it would require creation of a new class on both sides.

The zoom API specifically exists on both, the webFrame and webContents instances. However, I'm not sure how they actually affect each other (i.e., is the zoom synchronized or inherited?). For now, I would say adding the webContents-specific methods to the already existing WebContents class on the C# side would probably be more than enough.

@AeonSake commented on GitHub (Dec 4, 2025): > After looking closely at the code it appears we do not have `webFrame` class at all. Will take longer to implement since it would require creation of a new class on both sides. The zoom API specifically exists on both, the `webFrame` and `webContents` instances. However, I'm not sure how they actually affect each other (i.e., is the zoom synchronized or inherited?). For now, I would say adding the `webContents`-specific methods to the already existing `WebContents` class on the C# side would probably be more than enough.
Author
Owner

@agracio commented on GitHub (Dec 4, 2025):

Ok so I will be dropping webFrame for now. As for all the methods you mentioned I will implement some but definitely not all.
Would be great if everyone else had a look at this list and gave some feedback.

@agracio commented on GitHub (Dec 4, 2025): Ok so I will be dropping `webFrame` for now. As for all the methods you mentioned I will implement some but definitely not all. Would be great if everyone else had a look at this list and gave some feedback.
Author
Owner

@AeonSake commented on GitHub (Dec 4, 2025):

Yea, I feel like a lot of these methods are quite niche. I just tried to document most potentially relevant methods. Though personally, I would probably only add the zoom, the audio/mute, and maybe the dev tools and user agent stuff because they seem to be rather trivial for the implementation but still provide decent benefit for the user.

@AeonSake commented on GitHub (Dec 4, 2025): Yea, I feel like a lot of these methods are quite niche. I just tried to document most _potentially_ relevant methods. Though personally, I would probably only add the zoom, the audio/mute, and maybe the dev tools and user agent stuff because they _seem_ to be rather trivial for the implementation but still provide decent benefit for the user.
Author
Owner

@agracio commented on GitHub (Dec 5, 2025):

Some are done and merged to develop https://github.com/ElectronNET/Electron.NET/pull/958. I might take a look at more once I have some time.

@agracio commented on GitHub (Dec 5, 2025): Some are done and merged to develop https://github.com/ElectronNET/Electron.NET/pull/958. I might take a look at more once I have some time.
Author
Owner

@agracio commented on GitHub (Dec 6, 2025):

There will be changes to property names to make them async, please hold on before using them in any implementation.

@agracio commented on GitHub (Dec 6, 2025): There will be changes to property names to make them async, please hold on before using them in any implementation.
Author
Owner

@agracio commented on GitHub (Dec 6, 2025):

Update: all properties will be removed, only methods will remain in code. See discussion in https://github.com/ElectronNET/Electron.NET/pull/958

@agracio commented on GitHub (Dec 6, 2025): Update: all properties will be removed, only methods will remain in code. See discussion in https://github.com/ElectronNET/Electron.NET/pull/958
Author
Owner

@AeonSake commented on GitHub (Dec 30, 2025):

Not a bug (probably a feature) but caught me be my surprise: zoom factor is shared across all windows by default. There might also be other shared properties (e.g., clipboard, audio, etc.), but zoom factor is what I noticed first. To mitigate this, the easiest way I found is to ensure each window has a unique partition name set under BrowserWindowOptions.WebPreferences.Partition when creating the window. Just thought I'd leave this here if someone else came across a similar issue.

@AeonSake commented on GitHub (Dec 30, 2025): Not a bug (probably a feature) but caught me be my surprise: zoom factor is shared across all windows by default. There might also be other shared properties (e.g., clipboard, audio, etc.), but zoom factor is what I noticed first. To mitigate this, the easiest way I found is to ensure each window has a unique partition name set under `BrowserWindowOptions.WebPreferences.Partition` when creating the window. Just thought I'd leave this here if someone else came across a similar issue.
Author
Owner

@agracio commented on GitHub (Dec 31, 2025):

Does this happen on Get(), Set() or both?
Looking at code Set() passes Id but Get() does not, so looks like an error in PR on my part.
If both do not work then there is a separate issue.

@agracio commented on GitHub (Dec 31, 2025): Does this happen on `Get()`, `Set()` or both? Looking at code `Set()` passes `Id` but `Get()` does not, so looks like an error in PR on my part. If both do not work then there is a separate issue.
Author
Owner

@AeonSake commented on GitHub (Dec 31, 2025):

Does this happen on Get(), Set() or both? Looking at code Set() passes Id but Get() does not, so looks like an error in PR on my part. If both do not work then there is a separate issue.

To be fair, I haven't used the Get() method(s) at all since in my case, the zoom controls are entirely handled on the C# side. I only use window.WebContents.SetZoomFactor(value) to apply a zoom factor. In the video below, you can see how this propagates through all open Electron windows. Furthermore, if using the default partition name (i.e., not setting it), the zoom factor is also persisted, which I also didn't expect (usually, only partitions prefixed with persist: should be persisted). I didn't try to forcefully set a zoom factor when creating the window, though.
At the end of the video, you can see how it works as intended when selecting unique partition names. I still believe this is intended design by Electron itself since shared partitions are meant to also share data between windows (cache, local storage, etc.) and I assume it also includes parts of the window config space. I have not tested other parts of the webContents API for what all is shared across windows, though. Dev-Tools for one is unique to the individual window at least.

I have also read that changing the zoom factor of the webFrame instance will only affect the individual window content/frame, but since there is currently no C# API for the webFrame instance, I couldn't really test that further.

https://github.com/user-attachments/assets/a97731f9-7646-4e10-b946-18e5ac8890e3

@AeonSake commented on GitHub (Dec 31, 2025): > Does this happen on `Get()`, `Set()` or both? Looking at code `Set()` passes `Id` but `Get()` does not, so looks like an error in PR on my part. If both do not work then there is a separate issue. To be fair, I haven't used the `Get()` method(s) at all since in my case, the zoom controls are entirely handled on the C# side. I only use `window.WebContents.SetZoomFactor(value)` to apply a zoom factor. In the video below, you can see how this propagates through all open Electron windows. Furthermore, if using the default partition name (i.e., not setting it), the zoom factor is also persisted, which I also didn't expect (usually, only partitions prefixed with `persist:` should be persisted). I didn't try to forcefully set a zoom factor when creating the window, though. At the end of the video, you can see how it works as intended when selecting unique partition names. I still believe this is intended design by Electron itself since shared partitions are meant to also share data between windows (cache, local storage, etc.) and I assume it also includes _parts_ of the window config space. I have not tested other parts of the `webContents` API for what all is shared across windows, though. Dev-Tools for one is unique to the individual window at least. I have also read that changing the zoom factor of the `webFrame` instance will only affect the individual window content/frame, but since there is currently no C# API for the `webFrame` instance, I couldn't really test that further. https://github.com/user-attachments/assets/a97731f9-7646-4e10-b946-18e5ac8890e3
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/Electron.NET#1024