I'm a big fan of using the OpenAI Playground, and I have numerous saved templates for generating landing page headlines and checking my grammar. In this walkthrough, we'll build a Chrome extension so you don't have to leave your browser to ask GPT-3 questions.
We'll build the extension with ReactJS as the view engine, rendering a popup with your logic.
If you're interested in following this blog post as a course, I'm working on turning this popular guide on Chrome extension development using GPT-3 into a course. Sign up for our waitlist to be notified when it's available and have access to additional resources and guidance ➡️ https://gpt3-chrome-extension.carrd.co/
Disclaimer
Do not publish an app or an extension with any of your API keys; this tutorial is just a walkthrough of creating a Chrome extension.
Don't expose unencrypted API keys/secrets/credentials if you're planning to publish an app or an extension.
Here's what we'll use:
1. ReactJS ⚛️ 2. OpenAI SDK 🦾
And here's a summary of the steps we'll take:
1. Create a ReactJS app 2. Create extension updates 3. Install Material UI (optional) 4. Create the extension UI 5. Get OpenAI API key 6. Add OpenAI configuration 7. Create GPT-3 completion 8. Add favicon 9. Upload extension 10. UI fixes 11. Save the state (optional) 12. Ask away
Prerequisites:
- Node - npm
1. Create a ReactJS app
Let's start with creating a new React app. To begin, run this command within your favorite directory on your computer:
npx create-react-app your-app-name
This will start the creation of a regular React app and can take a couple of minutes. When the installation is complete, cd into the folder your-app-name and start the app:
cd your-app-name
npm start
This will open a new browser window with this starting point:
The next step is to install Material UI, which is a fully-loaded component library and design system https://mui.com/
2. Create extension updates
Now that we have a react app, we need to make 2 changes to the extension structure. The first step is to update the public/manifest.json
file. The manifest describes what the source package includes and the extension's permissions.
Open public/manifest.json
and change it to this structure:
{
"manifest_version": 3,
"name": "YOUR_EXTENSION_NAME",
"description": "DESCRIPTION",
"version": "1.0.0",
"action": {
"default_popup": "index.html",
"default_title": "Open the popup"
},
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"permissions": [
"scripting",
"activeTab",
"storage"
],
"host_permissions": [
"https://*/"
]
}
Then add the following snippet to your src/App.js
file right at the very top:
/*global chrome*/
Great, we're ready to start building the app.
3. Install Material UI (optional)
I'll use Material UI to format the UI in this guide; it is a fully-loaded component library and design system with production-ready components.
Start by installing the library, and run this command in the folder your-app-name that got created earlier:
npm install @mui/material @emotion/react @emotion/styled
Let's also go ahead and install the icon package, it consists of SVG icons exported as React components:
npm install @mui/icons-material
Now that we have Material UI installed let's start building the extension app.
4. Create the extension UI
In this section, we'll build a super simple app with just a text field and a button. The button will call the OpenAI SDK and ask GPT-3 a question. The response is then made visible below the text field.
Change the title
Start by changing the title of our Chrome extension. Open up public/index.html
and change the title
tag to your own name:
<title>GPT-3 Chrome extension</title>
Create React app comes with reloading and ES6 support, so you should already see the changes in the browser tab:
Right, go ahead and remove the already written code in src/App.js
and replace it with a Container and Grids. Your App.js
should now look like this:
import React, {useState} from "react";
import "./App.css";
import { Box, Container, Grid, TextField } from "@mui/material";
function App() {
return (
<Container>
<Box sx={{ width: "100%", mt: 4 }}>
<Grid container>
<Grid item xs={12}>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default App;
Continue with creating variables for the GPT-3 prompt:
const [prompt, setPrompt] = useState("")
Also, add this snippet to create a text field:
<TextField
autoFocus
fullWidth
label="Your text"
variant="outlined"
multiline
rows={4}
margin="normal"
value={prompt}
onChange={(e) => {
setPrompt(e.target.value);
}}
/>
Create this function to handle the API call:
async function handleSubmit() {
return
}
Then create a button calling the function:
<Button
fullWidth
disableElevation
variant="contained"
onClick={() => handleSubmit()}
>
Submit
</Button>
The app should now look like this:
And here's all the code in App.js
so far:
import React, { useState } from "react";
import "./App.css";
import { Box, Button, Container, Grid, TextField } from "@mui/material";
function App() {
const [prompt, setPrompt] = useState("");
async function handleSubmit() {}
return (
<Container>
<Box sx={{ width: "100%", mt: 4 }}>
<Grid container>
<Grid item xs={12}>
<TextField
fullWidth
autoFocus
label="Your text"
variant="outlined"
multiline
rows={4}
margin="normal"
value={prompt}
onChange={(e) => {
setPrompt(e.target.value);
}}
/>
<Button
fullWidth
disableElevation
variant="contained"
onClick={() => handleSubmit()}
>
Submit
</Button>
</Grid>
</Grid>
</Box>
</Container>
);
}
export default App;
Let's add another variable to track while the API call is made:
const [isLoading, setIsLoading] = useState(false)
Import an icon called AutoRenew:
import AutorenewIcon from '@mui/icons-material/Autorenew';
And add the icon to the button and add a disabled condition;
<Button
fullWidth
disableElevation
variant="contained"
disabled={isLoading}
onClick={() => handleSubmit()}
startIcon={
isLoading && (
<AutorenewIcon
sx={{
animation: "spin 2s linear infinite",
"@keyframes spin": {
"0%": {
transform: "rotate(360deg)",
},
"100%": {
transform: "rotate(0deg)",
},
},
}}
/>
)
}
>
Submit
</Button>
This snippet will make the icon spin while isLoading is true while the API is called.
Finally, let's add a Paper component under the button for the API response. Start with creating a new variable:
const [response, setResponse] = useState("")
And add a Paper component that gets populated once we have a response:
<Grid item xs={12} sx={{mt:3}}>
<Paper sx={{p:3}}>{response}</Paper>
</Grid>
We have a very simple UI finished, and we're ready to create the logic for sending an API request to OpenAI.
5. Get OpenAI API key
Before we go ahead and write the API call, let's get the OpenAI credentials needed for the API calls.
Go to https://beta.openai.com/, log in and click on your avatar and View API keys:
Then create a new secret key and save it for the request:
Then click on Settings
and save the Organization ID for your account:
Now we have all the credentials needed to make an API request.
6. Add OpenAI configuration
Install the OpenAI Node library:
npm install openai
Then import Configuration
and OpenAIApi
from the library:
import { Configuration, OpenAI } from "openai";
Disclaimer
Do not publish an app or an extension with any of your API keys; this tutorial is just a walkthrough of creating a Chrome extension.
Don't expose unencrypted API keys/secrets/credentials if you're planning to publish an app or an extension.
Initializing the library with the API key and organization id:
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
Now we're ready to create a completion.
7. Create GPT-3 completion
We're finally ready to finalize the handleSubmit
function with the API call to GPT-3 completion.
Update the handleSubmit
to this:
async function handleSubmit() {
setIsLoading(true);
try {
const completion = await openai.completions.create({
model: "text-davinci-002",
prompt: prompt,
max_tokens: 100,
});
setResponse(completion.data.choices[0].text);
setIsLoading(false);
} catch (e) {
alert("Error: ", e);
setIsLoading(false);
}
}
The response is saved in the response
variable and shown right under the text field:
Great, we have working logic to ask questions and show the results.
The last step is to create a favicon which will be the little icon for your Chrome extension, similar to these two:
8. Create a favicon
Let's create a favicon for our app, this will also be the little icon representing your Chrome extension.
Start by finding an image you like. I'm using this icon from https://icons8.com/ - remember to link to icons8.com if you're using icons for free. I have a monthly subscription which gives you 100 downloads/month.
Next up is to upload your image to https://realfavicongenerator.net/ - go through the steps and finally click on Generate your Favicons and HTML code:
Click to download your package once it is generated:
Unzip the package and copy the file favicon.ico
to your app folder public
, this will replace the current favicon.ico
:
Also copy one of the larger png
files to /public
, name it icon.png
as we wrote in the manifest earlier:
If you refresh the browser, you should see the new favicon:
Now that we have all the parts ready let's build and upload the extension to Chrome.
9. Upload extension
Start by running this command in the extension directory:
npm run build
The command will create a new /build
folder in the root folder of your extension directory; this is the folder we'll upload to Chrome.
Once the build is completed, visit chrome://extensions
in a new tab and click Load unpacked:
Then select the /build
folder that just got created:
There you have it! If you click on your extension menu in Chrome, you'll see your little icon, click on it, and our extension popup window should look something like this:
The only problem is that it is quite narrow. Let's fix that in the last section below.
10. UI fixes
Alrighty, the extension popup window is quite narrow, let's fix that. Open src/index.css
and add width and height to the body tag:
body {
width: 800px; /* Add width */
height: 1000px; /* Add height */
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
That's all. Run npm run build
again, then visit chrome://extensions/
and update the extension:
The app should now be this wide:
11. Save the state (optional)
The app is now fully working. One thing you'll notice is that once you click away from the popup, all the input data will disappear:
Here's how you can save the current state in local variables. Start by saving the prompt to the local state.
Add this snippet to the onChange in your TextField component:
onChange={(e) => {
setPrompt(e.target.value); // Keep
chrome.storage.local.set({ prompt: e.target.value }); // Add this
}}
This snippet will store the prompt to the local variable prompt.
Except for only saving the variable in the Chrome local storage, we also want to be able to add this value to your own variable prompt if you close the popup.
Do this by adding the useEffect hook to the src/App.js
file. Then add this snippet which looks for a variable prompt in the local Chrome variables and saves the value in your own variable prompt with useState
:
useEffect(() => {
try {
chrome.storage.local.get(null, function (data) {
if ("prompt" in data) {
setPrompt(data.prompt);
}
});
} catch (e) {
console.log("Error due to local state");
}
}, []);
The error message is for when you're running the app locally.
Now run npm run build
again, open chrome://extensions/
and update your extension. Your prompt is now saved locally when you close the popup:
That's everything, now you have a Chrome extension that works, and your input data is saved locally when you close your popup window. Let's try to ask some questions.
12. Ask away
Let's try the final app:
Summary
This was a quick and simple walkthrough to build your first Chrome extension.
Here are the steps we took:
1. Create a ReactJS app 2. Create extension updates 3. Install Material UI (optional) 4. Create the extension UI 5. Get OpenAI API key 6. Add OpenAI configuration 7. Create GPT-3 completion 8. Add favicon 9. Upload extension 10. UI fixes 11. Save the state (optional) 12. Ask away
Next steps
1. Repo with source code Here is the repo with the source code if you'd like to implement this on your own ⬇️ https://github.com/norahsakal/create-gpt3-chrome-extension
2. Do you need help with building your own Chrome extension? Or do you have other questions? I'm happy to help, don't hesitate to reach out ➡️ norah@quoter.se
3. Do you want this guide as a course? I'm working on turning this popular blog post on Chrome extension development using GPT-3 into a course.
If you're interested, you can sign up for our waitlist to be notified when it's available and have access to additional resources and guidance ⬇️ https://gpt3-chrome-extension.carrd.co/