Browser Integration
Enable type-safe communication between browser scripts using the Message Port Adapter.
Between Extension Scripts
To set up communication between scripts in a browser extension (e.g. background, content, popup), configure one script to listen for connections and upgrade them, and another to initiate the connection.
WARNING
The browser extension Message Passing API does not support transferring binary data, which means oRPC features like File
and Blob
cannot be used natively. However, you can temporarily work around this limitation by extending the RPC JSON Serializer to encode File
and Blob
as Base64.
import { experimental_RPCHandler as RPCHandler } from '@orpc/server/message-port'
const handler = new RPCHandler(router)
browser.runtime.onConnect.addListener((port) => {
handler.upgrade(port, {
context: {}, // provide initial context if needed
})
})
import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
const port = browser.runtime.connect()
const link = new RPCLink({
port,
})
INFO
This only shows how to configure the link. For full client examples, see Client-Side Clients.
Window to Window
To enable communication between two window contexts (e.g. parent and popup), one must listen and upgrade the port, and the other must initiate the connection.
import { experimental_RPCHandler as RPCHandler } from '@orpc/server/message-port'
const handler = new RPCHandler(router)
window.addEventListener('message', (event) => {
if (event.data instanceof MessagePort) {
handler.upgrade(event.data, {
context: {}, // Optional context
})
event.data.start()
}
})
window.open('/example/popup', 'popup', 'width=680,height=520')
import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
const { port1: serverPort, port2: clientPort } = new MessageChannel()
window.opener.postMessage(serverPort, '*', [serverPort])
const link = new RPCLink({
port: clientPort,
})
clientPort.start()
Advanced Relay Pattern
In some advanced cases, direct communication between scripts isn’t possible. For example, a content script running in the "MAIN" world cannot directly communicate with the background script using browser.runtime
or chrome.runtime
APIs.
To work around this, you can use a relay pattern typically an additional content script running in the default "ISOLATED" (default) world to relay messages between the two contexts. This relay pattern acts as an intermediary, enabling communication where direct access is restricted.
window.addEventListener('message', (event) => {
if (event.data instanceof MessagePort) {
const port = browser.runtime.connect()
// Relay `message` and `close/disconnect` events between the MessagePort and runtime.Port
event.data.addEventListener('message', (event) => {
port.postMessage(event.data)
})
event.data.addEventListener('close', () => {
port.disconnect()
})
port.onMessage.addListener((message) => {
event.data.postMessage(message)
})
port.onDisconnect.addListener(() => {
event.data.close()
})
event.data.start()
}
})
import { experimental_RPCHandler as RPCHandler } from '@orpc/server/message-port'
const handler = new RPCHandler(router)
browser.runtime.onConnect.addListener((port) => {
handler.upgrade(port, {
context: {}, // provide initial context if needed
})
})
import { experimental_RPCLink as RPCLink } from '@orpc/client/message-port'
const { port1: serverPort, port2: clientPort } = new MessageChannel()
window.postMessage(serverPort, '*', [serverPort])
const link = new RPCLink({
port: clientPort,
})
clientPort.start()