title: nodeIntegration vs preload.js vs IPC in Electron
body: <p>I've read through Electron's <a href="https://www.electronjs.org/docs/latest/tutorial/context-isolation" rel="nofollow noreferrer">context isolation</a>, <a href="https://www.electronjs.org/docs/latest/tutorial/ipc" rel="nofollow noreferrer">IPC</a>, and <a href="https://www.electronjs.org/docs/latest/tutorial/security" rel="nofollow noreferrer">security</a> docs, along with <a href="https://stackoverflow.com/questions/57505082/would-it-be-safe-to-enable-nodeintegration-in-electron-on-a-local-page-that-is-p">this post about using nodeIntegration</a>, and <a href="https://stackoverflow.com/questions/57807459/how-to-use-preload-js-properly-in-electron">this post about preload.js</a>. It looks like there are a lot of different ways to accomplish similar tasks and I'm not sure which is the best (safe, easy, etc.).</p>
  <p>I know you can simply enable <code>nodeIntegration</code> in renderer processes to access Node outside of the main process. Every source recommended against that for the most part.</p>
  <p>Here's where I'm confused. An example from Electron's documentation says you can do something like what's below.</p>
  <p>preload.js</p>
  <pre><code>// preload with contextIsolation disabled
  window.myAPI = {
    doAThing: () =&gt; {}
  }
  </code></pre>
  <p>renderer.js</p>
  <pre><code>// use the exposed API in the renderer
  window.myAPI.doAThing()
  </code></pre>
  <p>preload.js has access to Node APIs so technically I could load in all Node processes and then access them in my renderers.</p>
  <p>However, I also read about IPC.</p>
  <p>part of main.js</p>
  <pre><code>ipcMain.on('set-title', (event, title) =&gt; {
      const webContents = event.sender
      const win = BrowserWindow.fromWebContents(webContents)
      win.setTitle(title)
  })
  </code></pre>
  <p>preload.js</p>
  <pre><code>const { contextBridge, ipcRenderer } = require('electron')
  
  contextBridge.exposeInMainWorld('electronAPI', {
      setTitle: (title) =&gt; ipcRenderer.send('set-title', title)
  })
  </code></pre>
  <p>renderer.js</p>
  <pre><code>const setButton = document.getElementById('btn')
  const titleInput = document.getElementById('title')
  setButton.addEventListener('click', () =&gt; {
      const title = titleInput.value
      window.electronAPI.setTitle(title)
  });
  </code></pre>
  <p>For example, let's say I wanted to implement a function using an external npm module. Would I incorporate it in preload.js and call it from my renderer, or incorporate it in main.js, use ipcRenderer in preload.js with a channel for the function, and then call it from my renderer?</p>