Framework
Messaging

Messaging API

See LicenseNPM InstallFollow PlasmoHQ on TwitterWatch our Live DEMO every FridayJoin our Discord for support and chat about our projects

Plasmo's Messaging API makes communication between different parts of your extension easy. Add a file to your messages directory, and Plasmo will handle all the rest. Plasmo Messaging is a declarative, type-safe, functional, promise-based API for sending, relaying, and receiving messages between your extension components.

Installation

pnpm install @plasmohq/messaging

To use this library, you must use a directory for your background service worker. So if you've been adding it to your root, replace background.ts with background/index.ts

TL;DR

Messaging APIFromToOne-timeLong-lived
Message FlowExt-Pages/CSBGSWYesNo
Relay FlowWebsiteCS/BGSWYesNo
PortsExt-Pages/CSBGSWNoYes

There are a few paths we haven't implemented yet:

Messaging APIFromToOne-timeLong-lived
TBABGSWExt-Pages??
TBABGSWCS??
TBABGSWWebPage??

Examples

Message Flow

Use the Message Flow to initiate one-time messages between extension pages, tab pages or content scripts with the background service worker. This flow is useful to offload heavy computation to the background service worker or to bypass CORS.

The background service worker is a message hub with REST-style API handlers. To create a message handler, create a ts module in the background/messages directory. The file name should be the message name, and the default export should be the handler function:

background/messages/ping.ts
import type { PlasmoMessaging } from "@plasmohq/messaging"
 
export const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
  const message = await querySomeApi(req.body.id)
 
  res.send({
    message
  })
}

Extension pages, content scripts, or tab pages can send messages to these handlers using the @plasmohq/messaging library. Since Plasmo Framework orchestrates your handlers behind the scenes, the message names are typed and will enable IntelliSense in your editor:

popup.tsx
import { sendToBackground } from "@plasmohq/messaging"
 
...
const resp = await sendToBackground({
  name: "ping",
  body: {
    id: 123
  }
})
 
console.log(resp)

Relay Flow

⚠️

NOTE: The Relay messaging API is in public alpha preview: expects bugs, incomplete/leaky abstractions, and future API changes. Please report any issues you encounter to us via this link.

Use the Relay Flow to communicate between a target webpage and the background service worker. A relay is a lightweight message handler registered using a content script. It listens for messages from the target webpage and pipes them down to the Message Flow's handlers.

Create a relay inside a content script. The relay function takes a message name and an optional handler function. A content script can have multiple relays. Given the ping message handler from the previous example, and the website www.plasmo.com:

contents/plasmo.ts
import type { PlasmoContentScript } from "plasmo"
 
import { relay } from "@plasmohq/messaging"
 
export const config: PlasmoContentScript = {
  matches: ["http://www.plasmo.com/*"] // Only relay messages from this domain
}
 
relay(
  {
    name: "ping"
  },
  async (payload) => {
    console.log(payload)
  }
)
  • On the plasmo.com web page, you can send messages via the relay:
pages/index.tsx
 
import { sendViaRelay } from "@plasmohq/messaging"
...
 
const resp = await sendViaRelay({
  name: "ping"
})
 
console.log(resp)

Ports

⚠️

The Port messaging API is in public alpha preview: expects bugs, incomplete/leaky abstractions, and future API changes. Please report any issues you encounter to us via this link.

The Messaging Ports API is a high-level abstraction over the chrome runtime's port API (opens in a new tab) to establish long-lived connections with the background service worker.

The current implementation focuses on establishing connections to a port listener in the background service worker:

To create a BGSW port handler, create a ts module in the background/ports directory. The file name will be the port name, and the default export will be the handler function:

background/ports/mail.ts
import type { PlasmoMessaging } from "@plasmohq/messaging"
 
export const handler: PlasmoMessaging.PortHandler = async (req, res) => {
  console.log(req)
 
  res.send({
    message: "Hello from port handler"
  })
}

In your extension page, get the port using the getPort utility under the @plasmohq/messaging/port, OR use the usePort hook. The data will always reflect the latest response from the port handler:

tabs/delta.tsx
import { usePort } from "@plasmohq/messaging/hook"
 
function DeltaTab() {
  const mailPort = usePort("mail")
 
  return (
    <div>
      {mailPort.data?.message}
      <button
        onClick={async () => {
          mailPort.send({
            hello: "world"
          })
        }}>
        Send Data
      </button>
    </div>
  )
}
 
export default DeltaTab

E2E Type Safety (WIP)

End-to-end request/response body type-safety is in progress at #334 (opens in a new tab). In the meantime, you can use the provided generic types:

background/messages/ping.ts
import type { PlasmoMessaging } from "@plasmohq/messaging"
 
export type RequestBody = {
  id: number
}
 
export type ResponseBody = {
  message: string
}
 
export const handler: PlasmoMessaging.MessageHandler<
  RequestBody,
  ResponseBody
> = async (req, res) => {
  console.log(req.body.id)
 
  res.send({
    message: "Hello from background"
  })
}
popup.tsx
import { sendToBackground } from "@plasmohq/messaging"
 
import type { RequestBody, ResponseBody } from "~background/messages/ping"
 
...
 
const resp = await sendToBackground<RequestBody, ResponseBody>({
  name: "ping",
  body: {
    id: 123
  }
})
 
console.log(resp)
Last updated on December 27, 2022