Socials

Developing Browser Extensions with Next.js

4 Min Read
000000

Although modern browsers offer a plethora of tooling to help assist web development and general productivity, there will always be some niche solution(s) not offered. How do we solve this? Extensions!

For the following example, we will showcase how easy it is to bundle a Next.js application within a browser extension that’s compatible with all Chromium-based browsers (Chrome, Edge, Opera, Brave) and Firefox.

It’s worth mentioning that by using a rich framework like Next.js, we unlock the ablity to create and scale a more complex web application. All embedded within a browser extension.

Lets begin by creating our Next.js application:

yarn create next-app demo-nextjs-browser-extension  --typescript
 
# All Chromium-based browsers.
yarn add -D @types/chrome
 
# Firefox.
yarn add -D @types/firefox-webext-browser

Now that we have our application, let add some dimensions to our body tag. This will determine the size of the popup window when clicking on the extension’s icon within the browser’s toolbar.

The max dimensions allowed via Chromium browsers and Firefox is 800x600 pixels. Chromium Source | Firefox Source

styles/globals.scss
body {
  width: 500px;
  min-height: 600px;
}

Once our dimensions are in place, lets update the template within our application. For the following example, we will keep it simple but do note; we could make a much more complex application with the help of a framework like Next.js.

pages/index.tsx
import type { NextPage } from 'next';
 
const Home: NextPage = () => {
  return (
    <main>
      <section>
        <h1>Hello!</h1>
        <p>This is a Next.js application. 😎</p>
      </section>
    </main>
  );
};
 
export default Home;

Within all extensions, there exists a manifest.json file. Think of this file as the configuration file. It contains the extension’s metadata and security permissions as well as a pointer to our Next.js application. Let’s now add this file to our Next.js app’s /public directory:

Chrome
public/manifest.json
{
  "name": "Next.js Browser Extension Demo",
  "version": "1.0.0",
  "manifest_version": 3,
  "action": {
    "default_title": "Next.js Browser Extension Demo",
    "default_popup": "index.html"
  }
}
Firefox/Edge/Opera/Brave
public/manifest.json
{
  "name": "Next.js Browser Extension Demo",
  "version": "1.0.0",
  "manifest_version": 2,
  "browser_action": {
    "default_title": "Next.js Browser Extension Demo",
    "default_popup": "index.html"
  }
}

Before moving further, lets break down the contents of the manifest.json file. To begin, we have the name and version. Each are simply metadata for the extension and are required.

Next, there’s the manifest_version. This field is essential for defining which manifest.json version the browser should interpret. If your curious about which versions are compatibile with which browsers, check out the docs here .

Based on the manifest_version, the next field can be either action or browser_action. Each of which preforms the same action. This field’s content is responsible for creating the actual icon and title of the extension within the browser’s toolbar. It’s also responsible for creating a pointer to our Next.js application.

If your curious in learning more about manifest.json, consider checking out the official docs here .

Before testing our extension, we must first bundle the Next.js application. This can be done by running the following commands:

npx next build
npx next export

Once complete, we should see an /out directory. It contains the bundled version of our Next.js application as well as our manifest.json. Think of this directory as the source code of our extension.

If your using Firefox, please skip to the next section! The following only applies to Chromium-based browsers (Chrome, Edge, Opera, and Brave).

Before importing our extension into the browser, we must address one issue. Directories and files prefixed with an underscore (\_) are reserved by system.

This conflicts with a bundled Next.js application since its root directory is \_next. To resolve this issue, we can run the following command to replace all instances of ‘_next’ with ‘next’:

# Windows/Linux
mv ./out/_next ./out/next && cd ./out && grep -rl '/_next' * | xargs sed -i 's|/_next|/next|g'
 
# MacOS
mv ./out/_next ./out/next && cd ./out && grep -rli '_next' * | xargs -I@ sed -i '' 's|/_next|/next|g' @;

We’ve now successfully prepared our Next.js application to be embedded within an extension! Our last step is to simply import it into the browser to test:

Please note, we will not cover publishing an extension to the browser’s marketplace. However, there are plenty of resources available on this subject.

Chrome
  1. Navigate to: chrome://extensions
  2. Toggle on ‘Developer Mode’ in the top right corner.
  3. Click the ‘Load Unpacked’ button.
  4. Select your /out directory.
Firefox
  1. Navigate to: about:debugging#/runtime/this-firefox
  2. Click the ‘Load Temporary Add-on…’ button.
  3. Select your manifest.json file.
Edge
  1. Navigate to: edge://extensions/
  2. Toggle on ‘Developer Mode’ in the left panel.
  3. Click the ‘Load Unpacked’ button.
  4. Select your /out directory.
Opera
  1. Navigate to: opera://extensions
  2. Toggle on ‘Developer Mode’ in the top right corner.
  3. Click the ‘Load Unpacked’ button.
  4. Select your /out directory.
Brave
  1. Navigate to: brave://extensions
  2. Toggle on ‘Developer Mode’ in the top right corner.
  3. Click the ‘Load Unpacked’ button.
  4. Select your /out directory.

Overall, extensions are fantastic tools that undoubtedly excel web development and general productivity.

The requirements for building an extension are as simple as its gets: HTML/CSS/JavaScript… and a manifest.json. With this simplicity, it unlocks the capability to embed rich/complex web applications.

With a more rich/complex web application comes the ability to solve more niche solutions not offered by browsers out of the box.

demo-nextjs-browser-extension

This project is a Next.js application that can be bundled into a browser extension.

TypeScript
21
9