How I Built My First Chrome Extension With Nextjs
Table of contents
**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:
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.
- 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
mv out/_next out/next
Purpose: After the build is done, this command moves the directory
_next
(which is a subfolder inside theout
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
tonext
as Chrome Extensions do not support_
as the first character of filenames.
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