Skip to content

fix: Support page_action for Firefox MV3#2200

Draft
smashedr wants to merge 3 commits intowxt-dev:mainfrom
smashedr:fix_page_action
Draft

fix: Support page_action for Firefox MV3#2200
smashedr wants to merge 3 commits intowxt-dev:mainfrom
smashedr:fix_page_action

Conversation

@smashedr
Copy link

Overview

Fix to allow page_action on Firefox MV3.

Manual Testing

I have added the page_action to the wxt.config.ts and confirmed both chrome and firefox generate the proper manifest.json.

page_action: {
  default_popup: 'popup.html',
  default_icon: icons,
  show_matches: ['*://*/*'],
},

Related Issue

@netlify
Copy link

netlify bot commented Mar 18, 2026

Deploy Preview for creative-fairy-df92c4 ready!

Name Link
🔨 Latest commit 372aa04
🔍 Latest deploy log https://app.netlify.com/projects/creative-fairy-df92c4/deploys/69bab240771e2a0008ff378e
😎 Deploy Preview https://deploy-preview-2200--creative-fairy-df92c4.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions github-actions bot added the pkg/wxt Includes changes to the `packages/wxt` directory label Mar 18, 2026
@aklinker1
Copy link
Member

aklinker1 commented Mar 18, 2026

Nice change, I like it. Could you add a test to packages/wxt/src/core/utils/__tests__/manifest.test.ts to ensure firefox MV3 page_action gets added correctly? There should already be test cases for using action on chrome. You can run the tests with

cd packages/wxt
pnpm test manifest

@smashedr
Copy link
Author

I flipped the logic to != firefox and added page_action: {} directly to the firefox mv3 test.

I was even more unsure how to make this change, but it does pass. Open to suggestions here...

@aklinker1
Copy link
Member

aklinker1 commented Mar 18, 2026

I added another test that tests what happens when you set <meta name="manifest.type" content="page_action" /> in the popup HTML file (docs). This should also result in a page_action in the final manifest, but currently it does not.

The changes you've made so far prevent the page_action from being stripped out of the final manifest, but we also need to support the manifest.type meta because this is the "correct" way to tell WXT to use a page_action.

Can you update the implementation so the new test passes?

@aklinker1 aklinker1 changed the title fix: page_action on firefox mv3 fix: Support page_action for Firefox MV3 Mar 18, 2026
@smashedr
Copy link
Author

@aklinker1 Can you provide a control case for adding a page_action to an arbitrary page via meta tags?

Lets say I add a custom entrypoint, I will call it popout (not the popup). Currently, how would I end up with this manifest entry?

"page_action": {
  "default_popup": "popout.html",
  "default_title": "Page Action Title",
  "default_icon": {
    "16": "images/logo16.png",
    "32": "images/logo32.png",
    "48": "images/logo48.png",
    "96": "images/logo96.png",
    "128": "images/logo128.png"
  },
  "show_matches": ["*://*/*"]
}

I have tried adding this to the index.html for the popout entrypoint but never see a page_action in the manifest.

<meta
  name="manifest.default_icon"
  content='{
      "16":"images/logo16.png",
      "24":"images/logo24.png",
      "32":"images/logo32.png",
      "48":"images/logo48.png",
      "128":"images/logo128.png"
  }'
/>
<meta name="manifest.type" content="page_action" />
<meta name="manifest.show_matches" content="['*://*/*']" />
<meta name="manifest.default_title" content="Page Action Title" />

So is this not possible? If it is, how is it currently done?


Moving forward, the only place I see to get this done is in addEntrypoints. Not only that, I don't see exactly how to easily do this. The only way to generate a valid manifest from meta tags I see is to iterate all pages, and if its a page_action manually add keys from camelCase to snake_case...

entrypoints.forEach((item) => {
  if (item.options.type === 'page_action') {
    manifest['page_action'] = {
      // ...item.options,
      default_title: item.options.defaultTitle,
      default_icon: item.options.defaultIcon,
      show_matches: item.options.showMatches,
      hide_matches: item.options.hideMatches,
      default_popup: item.name + '.html',
    };
  }
});

While this generates the correct manifest entry from meta tags, it still does not pass the test...

@aklinker1
Copy link
Member

aklinker1 commented Mar 19, 2026

WXT is designed to only support one popup. So you can't name it popout or anything else, it has to be popup. Does firefox support both an action/browser_action and a page_action? AFAIK, you can only have one, but I guess I've never tried adding two...

So right now, you have to name the file popup.html or popup/index.html and set <meta name="manifest.type" content="page_action" />.

If you want more control over custom entrypoints and manifest generation, you need to use hooks or a WXT module.

https://wxt.dev/guide/essentials/wxt-modules.html#add-custom-entrypoints

@aklinker1
Copy link
Member

aklinker1 commented Mar 19, 2026

I confirmed that it is possible, and created a full example for how to do it in WXT: https://github.com/wxt-dev/examples/tree/main/examples/dual-firefox-action

I would recommend extensions stick to one action to make it easier to maintain your extension so it works on all browsers, but it is possible to do.

For this PR, don't worry about this case, instead, we just care about supporting the single popup. I can help implement the rest if you're not sure how to make the change.

@smashedr
Copy link
Author

Ok, I got ahead of myself there. To confirm, I don't actually need to support meta tags. The original fix I made works for adding page_action directly to the wxt.config.ts manifest and setting it to any page you want. This allows full control over your page_action.

page_action: {
  default_popup: 'popout.html',
  default_icon: icons,
  show_matches: ['*://*/*'],
},

So were back to just getting your new test to pass. I will revisit this today when I get a chance and let you know if I need more help.

@aklinker1
Copy link
Member

aklinker1 commented Mar 19, 2026

Correct. Your initial change prevented WXT from stripping page_action property out of Firefox MV3 manifests, which was half the changes necessary. The other half of the work that my new test covers is to convert the actionpage_action when the <meta> tag was set.

@aklinker1
Copy link
Member

aklinker1 commented Mar 19, 2026

Some more context about why I brought up the <meta> if I wasn't clear lol

Details

One of WXT's core design choices is around configuring each entrypoint's options in their own files, not somewhere else. Similar to how content script matches are defined in the .js/.ts entrypoint, popup options are specified in the popup's HTML file via the <meta> element. Only global options should be placed in the wxt.config.ts file.

https://wxt.dev/guide/essentials/entrypoints#defining-manifest-options

That said, WXT doesn't stop you from defining some things in the wxt.config.ts file, mostly to allow people to modify the whole system via hooks and WXT modules. Which is why in your testing, you could define the page action in your wxt.config.ts and it would show up even though that's not the recommended approach.

@smashedr
Copy link
Author

smashedr commented Mar 19, 2026

So, it should be noted that page_action is completely separate from the action/popup.

While you can set your page_action to point to the html entrypoint for the popup, it can technically be set to any page.

What this does, is creates an icon on the address bar when ever a show matches or show hide rule passes (can also be done programmatically).

The key for the file to load when the icon clicked is called default_popup because it works like a popup, but can be any arbitrary html file and is in no way tied too or related to the browser action, action, or default popup.

So if you want to support this with meta, we need to be able to set any html file as the page_action and it should have nothing to do with the action/popup.

That being said, the current fix, allowing me define the page_action in the wxt.config.ts manifest is a sensible workaround for now. It keep it in firefox builds and strips it in others.

For reference, here is an extension that has a page_action and action (popup). The page action is in the address bar and only shows when the show_matches an address in the address bar. In this case they point point to the popup.html, but the idea is to reduce toolbar clutter and only show icons on the sites their needed for site specific addons.

View Example

firefox-20260319-142926335

@smashedr
Copy link
Author

smashedr commented Mar 20, 2026

@aklinker1 I am currently confused on how to modify this because I don't quite understand how it is "expected" to work in this framework.

It seems that you have a popup, and you can set the popup to be an action or a page_action?

But the reality is, you can have an action that works like a popup. Or you can have a page_action that works like a popup, Or you can have both, or have neither.

So if you want a traditional action (toolbar icon) it would be an entry like this:

"action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "images/logo16.png",
      "24": "images/logo24.png",
      "32": "images/logo32.png",
      "48": "images/logo48.png",
      "128": "images/logo128.png"
    }
  },

And (firefox only) if you want a page_action (address bar icon) it would be a separate entry like this :

"page_action": {
  "default_popup": "popup.html",
  "default_icon": {
    "16": "images/logo16.png",
    "24": "images/logo24.png",
    "32": "images/logo32.png",
    "48": "images/logo48.png",
    "96": "images/logo96.png",
    "128": "images/logo128.png"
  },
  "show_matches": ["*://*/*"]
},

Which is currently achievable with the changes in this PR as outlined above. I am just not sure how to make the newly added test pass.

@aklinker1
Copy link
Member

aklinker1 commented Mar 20, 2026

Replying to your first comment:

So, it should be noted that page_action is completely separate from the action/popup.

This is not true. MV3 combined the browser_action and page_action into just the action because they are basically the same thing. The only difference is that page actions could be configured to be shown/hidden for specific URLs via static config.

A page action is just another type of action. While it is true you can have neither, one, or both, that doesn't change the fact that they behave the exact same way once visible - if an HTML file is hooked up to it, a "popup" is shown.

They all even have the same default_popup property. So when you say "completely separate", I don't understand what you mean. They are two separate properties in the manfiest, yes, but they're not "completely" different in how they're used or how they're setup.

So in WXT you have a single "popup" entrypoint that can be configured to be added to the manifest as an action, browser_action, or page_action.

While you can set your page_action to point to the html entrypoint for the popup, it can technically be set to any page.

The key for the file to load when the icon clicked is called default_popup because it works like a popup, but can be any arbitrary html file and is in no way tied too or related to the browser action, action, or default popup.

Everything you just said here is true for browser_action/action as well - WXT just defaults to loading the popup.html entrypoint for ALL actions, assuming you only have ONE popup and ONE action in your extension.

So if you want to support this with meta, we need to be able to set any html file as the page_action and it should have nothing to do with the action/popup.

For reference, here is an extension that has a page_action and action (popup). The page action is in the address bar and only shows when the show_matches an address in the address bar. In this case they point point to the popup.html, but the idea is to reduce toolbar clutter and only show icons on the sites their needed for site specific addons.

You seem really focused on the case where you have both an action and a page_action. This is an edge case that I do not want to promote in WXT - it's only possible in Firefox and it makes writing a cross-browser web extension really difficult since this is a high-level design decision that just doesn't work on other browsers.

As I have shown in the example I shared yesterday, it is possible to accomplish exactly what you just described, today, without the changes in this PR. It is possible to set meta properties on some other HTML entrypoint and treat it as the default_popup for a page_action, passing in the show_matches and other options from the meta properties.

https://github.com/wxt-dev/examples/blob/0f61ce4a03b3eaeccf44746138e7733243d8bccf/examples/dual-firefox-action/modules/add-page-action.ts#L16-L27

So I didn't view the issue or this PR as about adding support for including both a action and page action, you can already do that. I viewed this PR as adding support for respecting the <meta name="manifest.type" content="page_action" /> config when targetting MV3 + Firefox.


Replying to your second comment:

I am currently confused on how to modify this because I don't quite understand how it is "expected" to work in this framework.

It seems that you have a popup, and you can set the popup to be an action or a page_action?

But the reality is, you can have an action that works like a popup. Or you can have a page_action that works like a popup, Or you can have both, or have neither.

Honestly, I didn't know that you could have both before you opened the original issue. So I designed WXT around only supporting one action.

Even after learning it's possible to have two actions, I don't want to support that because in reality, if you want to release an extension to Chrome, you can't use both. So it's easier to design your extension around a single action and a single popup.

That being said, forget about adding support for two actions in this PR. That might be your use-case, but that's not how the issue was worded, nor what the PR description describes. If you want to discuss supporting two actions in WXT, create a new feature request issue. But as I've described, I don't plan on adding this feature to the core WXT package. However, I would be open to adding the example's code into an official WXT module (like auto-icons) that adds this functionality to WXT.


Let me describe what I'm looking for in this PR:

Currently, WXT only supports a single popup HTML entrypoint. You choose to attach this HTML file to an action as the default_popup based on the following logic:

  • For MV2 && <meta name="manifest.type" content="browser_action" />, use a browser_action
  • For MV2 && <meta name="manifest.type" content="page_action" />, use a page_action
  • Otherwise for MV3, use an action

This PR should modify the logic to support page_action in Firefox MV3 extensions:

  • For MV2 && <meta name="manifest.type" content="browser_action" />, use a browser_action
  • For (MV2 || (MV3 && Firefox)) && <meta name="manifest.type" content="page_action" />, use a page_action
  • Otherwise use an action

Is it clear now why I added the test I added?

Sorry for the long reply lol, it just felt like we are not on the same page at all, so I'm trying to explain my POV thoroughly.

@smashedr
Copy link
Author

This is not true. MV3 combined the browser_action and page_action into just the action because they are basically the same thing. The only difference is that page actions could be configured to be shown/hidden for specific URLs via static config.

This is not how it works in Firefox.

I only write cross-browser addons. In this case, page_action is only supported in Firefox. So the goal is to add a page_action to the manifest for firefox only and have it stripped in other browsers. I am ONLY using page_action as an extra manifest entry on firefox browsers as a convenience for those users.

The solution in this PR currently does this. I think we move forward with this as is, and not try to support anything else.
This allows optionally adding a page_action to the manifest and have it show up on Firefox, but also be removed on other non-supported browsers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg/wxt Includes changes to the `packages/wxt` directory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants