How I Built My First Chrome Extension With Nextjs

·

5 min read

**Disclaimer**: This ended up with a lot of errors that I could not find solutions to. It had to do with Nextjs injecting inline scripts and Chrome Extensions' content security policies. I hope someone else can figure out how. I combed through StackOverflow, Github, Chatgpt, and NextjsDocs but still haven't found a solution.
I eventually switched to [React + Vite](notion.so/civilgoody/How-I-Built-a-Chrome-E..) which worked perfectly.*

So I set out to build my first Chrome extension using Nextjs. I know the fastest way to learn and build anything new is to search for tutorials/guides on YouTube/Google to help me get up to speed. Only when I can't find any, would I switch to official docs.

Most times I do, so I build up from there 😎.

I found some articles on Google, but they all had various issues:

  • The article was outdated e.g. some commands provided no longer functioned.

  • Some provided what I would consider a pretty strenuous headstart or were paid.

I also found some starter templates but they were quite old e.g. they still used the pages directory.

Then I went on to check YouTube and found this gem of a video. Although they provided commands that could only run on Windows with a few outdated ones. I soon figured out how to get mine to work on Linux.

How to Create your first "Hello World" Extension.

Step 1: Install Nextjs

npx create-next-app@latest hello-world

These are the options I like to pick 🤗:

Step 2: Create Manifest File

The manifest.json The file is the entry point into the extension. It contains all information(metadata) about the extension, and your extension cannot function without it.

In the docs, you will find that these are the required manifest keys:

So I'll create mine similarly in /hello-world/extension/manifest.json:
Note: It is recommended that the action property be added to see a popup when our extension is clicked among other benefits.

{
  "name": "Hello World",
  "version": "1.0.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "index.html"
  }
}

You can visit the docs for more information about the manifest.

Step 3: Update HomePage

Delete the default Nextjs Template in /hello-world/src/app/page.tsx and add this:

export default function Home() {
  return (
    <div className="animate-bounce text-green-400">Hello World</div>
  );
}

Step 4: Update the Next Config

Update your nextConfig in the /hello-world/next.config.mjs. This is required for Nextjs to do a static export of your build output and was recently included in Next 14 over the next export command.

const nextConfig = {
"output": "export"
};

You may encounter the below error if there is any server-side code e.g. <Image/>

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-Q+8tPsjVtiDsjF/Cv8FMOpg2Yg91oKFKDAJat1PPb2g='), or a nonce ('nonce-...') is required to enable inline execution

Step 5: Update Build Scripts

In the hello-world/package.json file, update the build property to this:

Windows:

"build": "next build && mv out/_next out/next && robocopy ./extension ./out /E",

MacOS/Linux:

"build": "next build && mv out/_next out/next && rsync -a --exclude='.*' extension/ out/",

Explanation:

  1. next build

    • Purpose: This is the official Next.js command to build the application for production. It creates an optimized production version of your app, typically placing the built files in a .next folder.
  2. mv out/_next out/next

    • Purpose: After the build is done, this command moves the directory _next (which is a subfolder inside the out folder) to a new name, next.

      • out is the default output folder used when you export the Next.js app as a static site.

      • Renames _next to next as Chrome Extensions do not support _ as the first character of filenames.

  3. rsync -a --exclude='.*' extension/ out/

    • Purpose: This command uses rsync, a utility for efficiently transferring and synchronizing files between directories.

      • -a: Archive mode, which preserves permissions, symlinks, modification times, and other attributes.

      • --exclude='.*': This excludes hidden files (those starting with a dot, such as .git, .DS_Store) from being copied.

      • extension/: This is the source directory, likely containing additional files or assets.

      • out/: This is the destination directory, where the static files for the Next.js site reside after the build.

Step 6: Build the package

Build the application:

npm run build

Once the out folder appears. That confirms you did it right✅.

Step 7: Load the extension in a browser

Go to chrome://extensions in Google Chrome, turn on Developer mode, then three options will appear.

Click on Load unpacked and load the hello-world/out file generated in Step 6.

Finally, test your extension by clicking on the Extensions toolbar.

Yayy!!! You did it. Your first Chrome Extension, built with op Nextjs🤭.

Potential Error

☣️ Caution: Some Next.js features require a Node.js web server, so server-related features except next/image (which only works if you use a custom image loader like Cloudinary) and server components are unsupported by a Chrome extension.

Error That Caused My Back Out

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-Q+8tPsjVtiDsjF/Cv8FMOpg2Yg91oKFKDAJat1PPb2g='), or a nonce ('nonce-...') is required to enable inline execution

Sources