Recommended Free Tools
Build an Electron screenshot app as a four-step workflow: enumerate screens or windows in the main process, let the user choose a source, capture one frame for preview, then copy or save it. Keep source enumeration, clipboard access, and file writing behind a preload bridge with narrow IPC methods; do not give the renderer unrestricted Node.js or filesystem access.
The example below outlines the core implementation pattern and the decisions a production app must make. Electron’s capture APIs and OS permission behavior vary by platform and version, so validate the packaged app on every supported target rather than treating development mode as proof of correct behavior.
What the application needs to do
A useful first version should do one thing well: capture a still image of a screen or application window, show the image, and let the user copy or save it. The user should be able to cancel, recover from permission denial, and understand when a source is unavailable.
- Source selection: decide whether the picker offers full displays, windows, or both. Region selection and annotation are additional product features, not prerequisites of Electron capture.
- Capture: turn the selected source into a media stream, draw a frame to a canvas, and convert it to an image representation.
- Preview: show the result before an irreversible or potentially surprising action.
- Export: provide distinct Copy and Save actions, with clear format, destination, filename, and error behavior.
This article demonstrates the architecture and key code paths. The exact display-media selection mechanism should be checked against the Electron version and operating systems your app supports.
#1 Best Overall
- CRISP CLARITY: This 23.8″ Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
- WORK SEAMLESSLY: This sleek monitor is virtually bezel-free on three sides, so the screen looks even bigger for the viewer. This minimalistic design also allows for seamless multi-monitor setups that enhance your workflow and boost productivity
- A BETTER READING EXPERIENCE: For busy office workers, EasyRead mode provides a more paper-like experience for when viewing lengthy documents
Choose a capture flow
Electron documents two relevant approaches: build a custom picker around desktopCapturer, or mediate a browser getDisplayMedia request in the main process. The first gives the app direct control of picker presentation; the second routes a display-media request through Electron’s main process and can be evaluated with the available system-picker behavior. Electron labels the system-picker option experimental, so do not assume it behaves identically across Electron versions and target operating systems.
| Approach | Best fit | Trade-off |
|---|---|---|
Custom picker using desktopCapturer |
You need your own screen/window list, previews, and product-specific selection UI. | You own picker design, source validation, and platform testing. |
Mediated getDisplayMedia |
You want to handle a browser display-media request in the main process and assess system picker behavior. | System-picker behavior is experimental in Electron’s documentation and must be checked on the actual version and OS combinations in scope. |
For a custom picker, ask for thumbnails only when the UI displays them. Electron’s API allows thumbnail dimensions to be set to zero when previews are unnecessary, avoiding thumbnail work. Return a minimal source record to the renderer rather than exposing full Electron objects.
Set up secure process boundaries
Electron combines Chromium, Node.js, Electron, dependencies, and application code; a renderer compromise is more serious when privileged APIs are exposed broadly. Use a preload script and contextBridge to expose only specific actions. Keep filesystem access, clipboard writes, and source enumeration in the main process behind narrowly scoped IPC handlers.
Rank #2
- CRISP CLARITY: This 22 inch class (21.5″ viewable) Philips V line monitor delivers crisp Full HD 1920x1080 visuals. Enjoy movies, shows and videos with remarkable detail
- 100HZ FAST REFRESH RATE: 100Hz brings your favorite movies and video games to life. Stream, binge, and play effortlessly
- SMOOTH ACTION WITH ADAPTIVE-SYNC: Adaptive-Sync technology ensures fluid action sequences and rapid response time. Every frame will be rendered smoothly with crystal clarity and without stutter
- INCREDIBLE CONTRAST: The VA panel produces brighter whites and deeper blacks. You get true-to-life images and more gradients with 16.7 million colors
- THE PERFECT VIEW: The 178/178 degree extra wide viewing angle prevents the shifting of colors when viewed from an offset angle, so you always get consistent colors
A minimal window setup looks like this:
const { app, BrowserWindow } = require('electron');
const path = require('node:path');
function createWindow() {
const win = new BrowserWindow({
width: 1000,
height: 720,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
sandbox: true,
nodeIntegration: false
}
});
win.loadFile('index.html');
}
app.whenReady().then(createWindow);
Keep application code local and updated. Do not load untrusted remote code with Node integration enabled, and do not expose generic helpers such as “send any IPC channel.” If sandboxing or another setting must change for a specific dependency, assess that dependency and retain the narrowest viable boundary.
Enumerate sources and expose a small API
The following main-process pattern lists supported source types and sends only identifiers, names, and optional thumbnail data to the picker. Validate the selected ID against the sources available for the current capture operation; do not trust arbitrary renderer input.
const { ipcMain, desktopCapturer } = require('electron');
ipcMain.handle('capture:list-sources', async (_event, includePreviews) => {
const sources = await desktopCapturer.getSources({
types: ['screen', 'window'],
thumbnailSize: includePreviews
? { width: 240, height: 150 }
: { width: 0, height: 0 }
});
return sources.map(source => ({
id: source.id,
name: source.name,
thumbnail: includePreviews ? source.thumbnail.toDataURL() : null
}));
});
For a production picker, account for an empty result, a source disappearing between listing and capture, and the possibility that an OS-level permission has not been granted. Clear or refresh stale selection state instead of leaving the user with a nonfunctional Capture button.
Rank #3
- Clear visuals. Fluid motion: A 144Hz refresh rate and 1ms MPRT deliver smooth, tear‑free motion across work, gaming, and streaming for clearer, more fluid viewing.
- Eye comfort: TÜV Rheinland 3‑star* certification reduces harmful blue light while preserving stunning color quality without compromise. *TÜV Rheinland 3-star eye comfort certification.
- Wide viewing angle: Get consistent views across a wide 178° /178° viewing angle.
- In-Plane Switching (IPS): See excellent color accuracy and consistency across wide viewing angles with In-plane Switching (IPS) technology.
- Ultra-thin bezels: Maximize your viewing experience with thin bezels.
The preload should expose specific operations only:
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('captureApp', {
listSources: includePreviews =>
ipcRenderer.invoke('capture:list-sources', Boolean(includePreviews)),
saveImage: (dataUrl, format) =>
ipcRenderer.invoke('capture:save-image', { dataUrl, format }),
copyImage: dataUrl =>
ipcRenderer.invoke('capture:copy-image', { dataUrl })
});
Do not expose ipcRenderer itself or let the renderer choose arbitrary channel names. Validate all data again in the main process: renderer-side checks improve usability, but they are not a security boundary.
Capture a still frame and preview it
Once the user has selected a source, obtain its display-media stream using the mechanism documented for your Electron version, then render one frame to a canvas. The capture is a media-stream operation followed by frame extraction; it is not inherently a single screenshot call. A canvas-based extraction in the renderer can follow this pattern after stream has been acquired:
Rank #4
- CURVED FOR ENHANCED ENGAGEMENT: An immersive viewing experience with a curved monitor that wraps more closely around your field of vision; It creates a wider view, enhancing depth perception and minimizing peripheral distraction
- SMOOTH PERFORMANCE FOR SEAMLESS CONTENT: Stay in the action when playing games, watching videos, or working on creative projects; The 100Hz refresh rate reduces lag and motion blur so you don't miss a thing in fast-paced moments¹
- MORE GAMING POWER: Gain the edge with optimizable game settings; Color and image contrast can be adjusted to see scenes more vividly and spot enemies hiding in the dark; Game Mode adjusts any game to fill the screen so you can view every detail²
- KEEP IT EASY ON THE EYES: Care for your eyes and stay comfortable, even during long sessions; Advanced eye comfort technology certified by TÜV reduces eye strain by minimizing blue light and reducing irritating screen flicker²
- INCREASED VERSATILITY: Connect to more; Plug devices straight into your monitor for increased flexibility, making your computing environment even more convenient
async function frameToPng(stream) {
const video = document.createElement('video');
video.srcObject = stream;
video.muted = true;
await video.play();
await new Promise(resolve => {
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) {
resolve();
} else {
video.addEventListener('loadeddata', resolve, { once: true });
}
});
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
if (!canvas.width || !canvas.height) {
throw new Error('The selected source has no usable video frame.');
}
canvas.getContext('2d').drawImage(video, 0, 0);
const dataUrl = canvas.toDataURL('image/png');
for (const track of stream.getTracks()) track.stop();
video.srcObject = null;
return dataUrl;
}
Connect the returned data URL to an image element for review, and keep the preview available until the user chooses Copy, Save, or Cancel. In real code, put stream shutdown in a finally block so tracks are stopped even if playback or canvas conversion fails. Also handle permission rejection, an ended track, zero video dimensions, and asynchronous capture errors in the UI. The snippet is an implementation pattern built on Electron’s documented stream capture, not a tested sample project or a guarantee that a particular source-selection prompt will appear on every platform.
Copy or save the image
Electron’s nativeImage is a shared image abstraction, and its clipboard API documents PNG and JPEG image types. Convert and validate the image in the main process, then keep the two user actions separate: copying is optimized for pasting elsewhere, while saving needs a destination and a clear filename/format policy.
A main-process clipboard handler can be shaped like this:
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsBest Value
- 【INTEGRATED SPEAKERS】Whether you're at work or in the midst of an intense gaming session, our built-in speakers provide rich and seamless audio, all while keeping your desk clutter-free.
- 【EASY ON THE EYES】 Protect your eyes and enhance your comfort with Blue-Light Shift technology. This feature reduces harmful blue light emissions from your screen, helping to alleviate eye strain during long hours of use and promoting healthier viewing habits.
- 【WIDEN YOUR PERSPECTIVE】Our sleek minimal bezel design ensures undivided attention. The nearly bezel-free display seamlessly connects in a dual monitor arrangement, delivering an unobstructed view that lets you focus on more at once, completely distraction-free.
const { ipcMain, clipboard, nativeImage } = require('electron');
ipcMain.handle('capture:copy-image', async (_event, { dataUrl }) => {
if (typeof dataUrl !== 'string' || !dataUrl.startsWith('data:image/png;')) {
throw new Error('Expected a PNG image.');
}
const image = nativeImage.createFromDataURL(dataUrl);
if (image.isEmpty()) throw new Error('The image could not be decoded.');
clipboard.writeImage(image);
return { copied: true };
});
For saving, show a native save dialog in the main process, restrict supported extensions to formats your app actually writes, and report cancellation separately from failure. For example, a PNG-only first version can use nativeImage.toPNG() and write the resulting bytes to the path returned by the save dialog. Avoid accepting an arbitrary path from the renderer. If you offer JPEG, specify how quality is chosen and ensure the saved extension matches the encoded format.
Platform permissions and behavior to test
- macOS: macOS 10.15 and later requires user consent for screen-content capture. Test the permission prompt and subsequent capture in the packaged application as well as during development.
- Linux with PipeWire: Electron documents a single returned source behavior. When both screen and window types are requested, the selected source can be a window capture. Design the picker and its empty/limited state accordingly.
- Audio: A still screenshot does not require audio capture. If you add audio, treat it as a separate feature with separate permissions and platform testing. Electron documents
NSAudioCaptureUsageDescriptionfor audio capture on macOS 14.2 and later. - All targets: Check enumeration, permissions, capture output, clipboard, save location, and cancellation for every supported OS and Electron version. These notes do not cover every distribution, compositor, or version difference.
Test the packaged workflow
- Start with a normal development build and verify that the source list contains the source types your app promises.
- Try capture for each source type, then verify the preview dimensions and image contents.
- Test cancellation at source selection and save-dialog stages; cancellation should not be shown as a crash or successful save.
- Copy an image and paste it into another application. Verify that the clipboard contains an image rather than a text URL.
- Save each supported format, reopen the file, and check the extension, dimensions, and image encoding.
- Build and install the packaged app on each target OS. Repeat permission, capture, copy, save, and cancellation tests there.
- Exercise failure cases: deny screen permission, return no sources, select a source that vanishes, and simulate a failed conversion or write. The UI should explain the failed step and offer a retry where appropriate.
Common problems and fixes
| Symptom | Likely cause | What to check |
|---|---|---|
| No sources appear | Permission denial, unsupported environment behavior, or a source-listing failure. | Surface the actual error, check OS privacy settings, and test the packaged build. On Linux with PipeWire, account for the documented single-source behavior. |
| Picker lists an item but capture fails | The source became stale, selection was not validated, or the chosen stream mechanism differs for the Electron/OS combination. | Refresh the source list and validate the ID in the main process; verify the version-specific display-media flow. |
| Preview is blank or zero-sized | The video frame was read before it was ready, the stream ended, or the source yielded no usable dimensions. | Wait for frame readiness, check video dimensions, stop tracks on error, and report that the capture did not produce an image. |
| Copy does nothing | The image data URL is invalid, image decoding failed, or clipboard handling is not reached. | Validate MIME type and decoded nativeImage; return an explicit success or error response to the UI. |
| File has the wrong type or will not open | Extension and encoding disagree, or save cancellation/error is treated as success. | Write bytes in the selected encoding, match extension to format, and distinguish canceled dialogs from write errors. |
| Works in development but not after packaging | OS permissions or packaged application configuration differ from development. | Test installed builds on target OSes and verify permission configuration and app identity where applicable. |
Or skip the browser setup
If your goal is to capture a website rather than implement desktop screen capture, ScreenshotNeo is a website screenshot API and MCP server—not a replacement for capturing arbitrary desktop windows. One GET request returns an image or PDF; its cleanup options accept cookie/consent banners and remove known consent platforms, newsletter popups, and chat widgets before capture. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, with response headers indicating the page verdict and billing status. AI agents can use its MCP server tools, including take_screenshot, get_page_info, and capture_pdf. Its free plan includes 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. See the ScreenshotNeo site and API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Sign up free for 1,000 screenshots a month, with no card required.
Frequently Asked Questions
Does an Electron screenshot app need audio permission?
No, not for a still screenshot. Audio capture is a separate feature with separate platform requirements.
Free tools Windows power users keep installed
One-click scans. No signup required.
Can Electron capture only part of a screen?
The workflow here selects a screen or window source. Region selection needs additional app behavior beyond this source-picker flow.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

