diff --git a/docs/tutorial/message-ports.md b/docs/tutorial/message-ports.md
index 9f1e9bf467aff..b236f4133e450 100644
--- a/docs/tutorial/message-ports.md
+++ b/docs/tutorial/message-ports.md
@@ -8,8 +8,7 @@ your app.
Here is a very brief example of what a MessagePort is and how it works:
-```js
-// renderer.js ///////////////////////////////////////////////////////////////
+```js title='renderer.js (Renderer Process)'
// MessagePorts are created in pairs. A connected pair of message ports is
// called a channel.
const channel = new MessageChannel()
@@ -28,8 +27,7 @@ port2.postMessage({ answer: 42 })
ipcRenderer.postMessage('port', null, [port1])
```
-```js
-// main.js ///////////////////////////////////////////////////////////////////
+```js title='main.js (Main Process)'
// In the main process, we receive the port.
ipcMain.on('port', (event) => {
// When we receive a MessagePort in the main process, it becomes a
@@ -84,14 +82,84 @@ process, you can listen for the `close` event by calling `port.on('close',
## Example use cases
+### Setting up a MessageChannel between two renderers
+
+In this example, the main process sets up a MessageChannel, then sends each port
+to a different renderer. This allows renderers to send messages to each other
+without needing to use the main process as an in-between.
+
+```js title='main.js (Main Process)'
+const { BrowserWindow, app, MessageChannelMain } = require('electron')
+
+app.whenReady().then(async () => {
+ // create the windows.
+ const mainWindow = new BrowserWindow({
+ show: false,
+ webPreferences: {
+ contextIsolation: false,
+ preload: 'preloadMain.js'
+ }
+ })
+
+ const secondaryWindow = BrowserWindow({
+ show: false,
+ webPreferences: {
+ contextIsolation: false,
+ preload: 'preloadSecondary.js'
+ }
+ })
+
+ // set up the channel.
+ const { port1, port2 } = new MessageChannelMain()
+
+ // once the webContents are ready, send a port to each webContents with postMessage.
+ mainWindow.once('ready-to-show', () => {
+ mainWindow.webContents.postMessage('port', null, [port1])
+ })
+
+ secondaryWindow.once('ready-to-show', () => {
+ secondaryWindow.webContents.postMessage('port', null, [port2])
+ })
+})
+```
+
+Then, in your preload scripts you receive the port through IPC and set up the
+listeners.
+
+```js title='preloadMain.js and preloadSecondary.js (Preload scripts)'
+const { ipcRenderer } = require('electron')
+
+ipcRenderer.on('port', e => {
+ // port received, make it globally available.
+ window.electronMessagePort = e.ports[0]
+
+ window.electronMessagePort.onmessage = messageEvent => {
+ // handle message
+ }
+})
+```
+
+In this example messagePort is bound to the `window` object directly. It is better
+to use `contextIsolation` and set up specific contextBridge calls for each of your
+expected messages, but for the simplicity of this example we don't. You can find an
+example of context isolation further down this page at [Communicating directly between the main process and the main world of a context-isolated page](#communicating-directly-between-the-main-process-and-the-main-world-of-a-context-isolated-page)
+
+That means window.messagePort is globally available and you can call
+`postMessage` on it from anywhere in your app to send a message to the other
+renderer.
+
+```js title='renderer.js (Renderer Process)'
+// elsewhere in your code to send a message to the other renderers message handler
+window.electronMessagePort.postmessage('ping')
+```
+
### Worker process
In this example, your app has a worker process implemented as a hidden window.
You want the app page to be able to communicate directly with the worker
process, without the performance overhead of relaying via the main process.
-```js
-// main.js ///////////////////////////////////////////////////////////////////
+```js title='main.js (Main Process)'
const { BrowserWindow, app, ipcMain, MessageChannelMain } = require('electron')
app.whenReady().then(async () => {
@@ -129,8 +197,7 @@ app.whenReady().then(async () => {
})
```
-```html
-
+```html title='worker.html'
```
-```html
-
+```html title='app.html'