{"id":21059104,"url":"https://github.com/ganeshrvel/tutorial-electron-window-switching","last_synced_at":"2026-05-19T01:38:52.081Z","repository":{"id":110981996,"uuid":"169058019","full_name":"ganeshrvel/tutorial-electron-window-switching","owner":"ganeshrvel","description":"Prevent duplicate window instance invoked by the main and renderer processes in an electron app ","archived":false,"fork":false,"pushed_at":"2019-02-05T05:08:30.000Z","size":7,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-20T19:51:18.526Z","etag":null,"topics":["electron","node","nodejs","renderer"],"latest_commit_sha":null,"homepage":"https://github.com/ganeshrvel/tutorial-electron-window-switching","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ganeshrvel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-02-04T09:56:56.000Z","updated_at":"2021-01-11T13:09:21.000Z","dependencies_parsed_at":null,"dependency_job_id":"c7d66e5b-8b3e-4d38-8257-bcdc7ba20e45","html_url":"https://github.com/ganeshrvel/tutorial-electron-window-switching","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganeshrvel%2Ftutorial-electron-window-switching","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganeshrvel%2Ftutorial-electron-window-switching/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganeshrvel%2Ftutorial-electron-window-switching/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ganeshrvel%2Ftutorial-electron-window-switching/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ganeshrvel","download_url":"https://codeload.github.com/ganeshrvel/tutorial-electron-window-switching/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243505953,"owners_count":20301617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["electron","node","nodejs","renderer"],"created_at":"2024-11-19T17:09:55.422Z","updated_at":"2026-05-19T01:38:52.026Z","avatar_url":"https://github.com/ganeshrvel.png","language":null,"funding_links":["https://paypal.me/ganeshrvel"],"categories":[],"sub_categories":[],"readme":"# Prevent duplicate instances of the same window, invoked by different processes, in an electron app.\n\n- Author: [Ganesh Rathinavel](https://www.linkedin.com/in/ganeshrvel \"Ganesh Rathinavel\")\n- License: [MIT](https://github.com/ganeshrvel/tutorial-electron-window-switching/blob/master/LICENSE \"MIT\")\n- Website URL: [https://github.com/ganeshrvel/tutorial-electron-window-switching](https://github.com/ganeshrvel/tutorial-electron-window-switching/ \"https://github.com/ganeshrvel/tutorial-electron-window-switching\")\n- Repo URL: [https://github.com/ganeshrvel/tutorial-electron-window-switching](https://github.com/ganeshrvel/tutorial-electron-window-switching/ \"https://github.com/ganeshrvel/tutorial-electron-window-switching\")\n- Contacts: ganeshrvel@outlook.com\n\n### The challenge\nIn an electron app, two or more operating system level processes run concurrently — the \"main\" and \"renderer\" processes.\n\nBecause these processes are isolated from each other, child window instance, has to be loaded separately for both - main and renderer processes. This phenomenon raises another issue— duplicate windows for the same URL origin\n\nI have spent a significant amount of time researching on preventing the issue of duplication of windows for the same URL origin. This was originally implemented inside [OpenMTP - Advanced Android File Transfer Application for macOS](https://github.com/ganeshrvel/openmtp \"OpenMTP - Advanced Android File Transfer Application for macOS\").\n\n###  Implementation:\n\nHere, as an example, I will be creating a privacy policy window which can be invoked by both menu item and as well as an anchor tag.\n\n- Install packages\n\n```shell\n$ npm install react-helmet\n\nor\n\n$ yarn add react-helmet\n```\n\n- Create a file *create-windows.js* and add the below code\n\n```javascript\nimport { BrowserWindow, remote } from 'electron';\nconst PRIVACY_POLICY_PAGE_TITLE = `Privacy Policy`; // this will be used as an identifier to capture the same browser instance\nlet privacyPolicyWindow = null;\n\n/**\n * Privacy Policy Window\n */\n\nconst undefinedOrNull = _var =\u003e {\n  return typeof _var === \"undefined\" || _var === null;\n};\n\nconst loadExistingWindow = (allWindows, title) =\u003e {\n  if (!undefinedOrNull(allWindows)) {\n    for (let i = 0; i \u003c allWindows.length; i += 1) {\n      const item = allWindows[i];\n      if (item.getTitle().indexOf(title) !== -1) {\n        item.focus();\n        item.show();\n\n        return item;\n      }\n    }\n  }\n\n  return null;\n};\n\nconst createWindow = isRenderedPage =\u003e {\n  const config = {\n    width: 800,\n    height: 600,\n    minWidth: 600,\n    minHeight: 400,\n    show: false,\n    resizable: true,\n    title: `${PRIVACY_POLICY_PAGE_TITLE}`,\n    minimizable: true,\n    fullscreenable: true,\n    webPreferences: {\n      nodeIntegration: true\n    }\n  };\n\n  // incoming call from a rendered page\n  if (isRenderedPage) {\n    const allWindows = remote.BrowserWindow.getAllWindows();\n\n    return loadExistingWindow(allWindows, PRIVACY_POLICY_PAGE_TITLE)\n      ? null\n      : new remote.BrowserWindow(config);\n  }\n\n  // incoming call from the main process\n  const allWindows = BrowserWindow.getAllWindows();\n\n  return loadExistingWindow(allWindows, PRIVACY_POLICY_PAGE_TITLE)\n    ? null\n    : new BrowserWindow(config);\n};\n\nexport const privacyPolicyCreateWindow = (isRenderedPage = false) =\u003e {\n  try {\n    if (privacyPolicyWindow) {\n      privacyPolicyWindow.focus();\n      privacyPolicyWindow.show();\n      return privacyPolicyWindow;\n    }\n\n    // show the existing privacyPolicyWindow\n    const _privacyPolicyWindowTemp = createWindow(isRenderedPage);\n    if (!_privacyPolicyWindowTemp) {\n      return privacyPolicyWindow;\n    }\n\n    privacyPolicyWindow = _privacyPolicyWindowTemp;\n    privacyPolicyWindow.loadURL(\n      `file://path/to/my-app/app/index.html#privacyPolicyPage`\n    ); // @todo: change the path accordingly\n\t\n    privacyPolicyWindow.webContents.on(\"did-finish-load\", () =\u003e {\n      privacyPolicyWindow.show();\n      privacyPolicyWindow.focus();\n    });\n\n    privacyPolicyWindow.onerror = error =\u003e {\n      console.error(error, `createWindows -\u003e privacyPolicyWindow -\u003e onerror`);\n    };\n\n    privacyPolicyWindow.on(\"closed\", () =\u003e {\n      privacyPolicyWindow = null;\n    });\n\n    return privacyPolicyWindow;\n  } catch (e) {\n    console.error(e, `createWindows -\u003e privacyPolicyWindow`);\n  }\n};\n```\n\n- Edit your *menu.js* file\n\n```javascript\nimport { privacyPolicyWindow } from './create-windows';\n\n// Add to your menu\nsubmenu: [\n\t{\n\t\tlabel: 'Privacy Policy from main process',\n\t\tclick: () =\u003e {\n\t\t\tprivacyPolicyWindow(false);\n\t\t}\n\t}\n]\n```\n\n- The *PrivacyPolicyPage/index.jsx*\n\n```javascript\nimport React, { Component } from 'react';\nimport { Helmet } from 'react-helmet';\n\nconst PRIVACY_POLICY_PAGE_TITLE = `Privacy Policy`;\n\nclass PrivacyPolicyPage extends Component {\n  render() {\n    return (\n      \u003cdiv\u003e\n        \u003cHelmet titleTemplate={`%s`}\u003e\n          \u003ctitle\u003e{PRIVACY_POLICY_PAGE_TITLE}\u003c/title\u003e\n        \u003c/Helmet\u003e\n\t\t\u003cdiv\u003e\n\t\t\t\u003cp\u003eWindow body\u003c/p\u003e\n\t\t\u003c/div\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n\nexport default PrivacyPolicyPage;\n```\n\n- Edit your router file\n\n```javascript\nimport PrivacyPolicyPage from './PrivacyPolicyPage';\n\n//Add the route\n\n\u003cSwitch\u003e\n\t\u003cRoute\n\t\tkey={PrivacyPolicyPage}\n\t\tpath='/privacyPolicyPage'\n\t\texact\n\t\tcomponent={PrivacyPolicyPage}\n\t\t/\u003e\n\u003c/Switch\u003e\n```\n\n- Add an anchor tag inside your home page\n\n```javascript\nimport { privacyPolicyWindow } from './create-windows';\n\n\u003ca\n\tclassName={styles.a}\n\tonClick={() =\u003e {\n\tprivacyPolicyWindow(true);\n\t}}\n\t\u003e\n\tPrivacy Policy from the renderer process\n\u003c/a\u003e\n```\n\n### Conclusion\n\n- call **privacyPolicyCreateWindow(true)** from renderer process and call **privacyPolicyCreateWindow(false)** from the main process.\n- *privacyPolicyCreateWindow* method will look up for the existing windows which match the title string and it returns the pre-existing window if it finds any.\n\n\n### Clone\n```shell\n$ git clone --depth 1 --single-branch --branch master https://github.com/ganeshrvel/tutorial-electron-window-switching.git\n\n$ cd tutorial-electron-window-switching\n```\n\n### Contribute\n- Fork the repo and create your branch from master.\n- Ensure that the changes pass linting.\n- Update the documentation if needed.\n- Make sure your code lints.\n- Issue a pull request!\n\nWhen you submit code changes, your submissions are understood to be under the same [MIT License](https://github.com/ganeshrvel/tutorial-electron-window-switching/blob/master/LICENSE \"MIT License\") that covers the project. Feel free to contact the maintainers if that's a concern.\n\n\n### Buy me a coffee\nHelp me keep the app FREE and open for all.\nPaypal me: [paypal.me/ganeshrvel](https://paypal.me/ganeshrvel \"paypal.me/ganeshrvel\")\n\n### Contacts\nPlease feel free to contact me at ganeshrvel@outlook.com\n\n### More repos\n- [OpenMTP  - Advanced Android File Transfer Application for macOS](https://github.com/ganeshrvel/openmtp \"OpenMTP  - Advanced Android File Transfer Application for macOS\")\n- [Tutorial Series by Ganesh Rathinavel](https://github.com/ganeshrvel/tutorial-series-ganesh-rathinavel \"Tutorial Series by Ganesh Rathinavel\")\n- [npm: electron-root-path](https://github.com/ganeshrvel/npm-electron-root-path \"Get the root path of an Electron Application\")\n- [Electron React Redux Advanced Boilerplate](https://github.com/ganeshrvel/electron-react-redux-advanced-boilerplate \"Electron React Redux Advanced Boilerplate\")\n\n### License\ntutorial-electron-window-switching is released under [MIT License](https://github.com/ganeshrvel/tutorial-electron-window-switching/blob/master/LICENSE \"MIT License\").\n\nCopyright © 2018-Present Ganesh Rathinavel\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fganeshrvel%2Ftutorial-electron-window-switching","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fganeshrvel%2Ftutorial-electron-window-switching","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fganeshrvel%2Ftutorial-electron-window-switching/lists"}