Introduction
At TalentSight, we've built a web platform that integrates deeply with LinkedIn. However, we noticed our recruiters were spending most of their time directly on LinkedIn rather than our platform. This led us to an important realization: we needed to bring our tools to where our users actually work.
The solution? A Chrome extension that embeds custom buttons and actions directly into LinkedIn, allowing recruiters to use TalentSight's features without ever leaving their workflow. In this tutorial, I'll walk you through how we built this using the Plasmo framework, and show you how to create your own Chrome extension.
What is Plasmo?
Plasmo is a browser extension SDK that dramatically simplifies extension development. Instead of wrestling with Chrome's complex manifest files and messaging APIs, Plasmo provides:
- React component support out of the box
- Built-in handling of pop-ups and messaging APIs
- Automatic management of browser APIs
- Hot module replacement for faster development
- TypeScript support by default
Think of it as the Next.js of browser extensions. It handles the boilerplate so you can focus on building features.
Getting Started with Plasmo
Initial Setup
To create a new Plasmo project, run the following command with your preferred package manager:
npm create plasmo
# or
pnpm create plasmo
# or
yarn create plasmo
This scaffolds a complete project structure with everything you need to get started.
Project Structure
After scaffolding, you'll see several key files:
- popup.tsx - The React component that displays when users click your extension icon
- assets/ - Where you store icons and images for your extension
- package.json - Contains dependencies and web manifest fields needed for publishing
The beauty of Plasmo is that it automatically generates the manifest.json file from your package.json, eliminating manual configuration headaches.
Development Workflow
One of Plasmo's best features is live reloading. Start the development server with:
npm run dev
This enables hot module replacement, meaning you can make changes to your code and see them instantly without rebuilding or reloading the extension manually. This is a massive time-saver during development.
Building for Production
When you're ready to package your extension for production:
npm run build
This creates a production-ready build in the build/ folder, optimized for your target browser and environment.
Loading Your Extension in Chrome
Before you can test your extension, you need to load it into Chrome:
- Navigate to
chrome://extensionsin your browser - Enable Developer mode using the toggle in the top right
- Click the Load unpacked button that appears
- Navigate to your project's
build/chrome-mv3-devfolder (orbuild/chrome-mv3-prodfor production builds) - Select the folder to load your extension
Your extension icon should now appear in Chrome's toolbar.
Extension Pages in Plasmo
Plasmo supports several types of extension pages, each serving a specific purpose:
Popup Page (popup.tsx)
The popup appears when users click your extension icon. This is typically your extension's main interface.
function IndexPopup() {
return (
<div className="popup-container">
<h1>Welcome to My Extension</h1>
<button>Take Action</button>
</div>
)
}
export default IndexPopup
Options Page (options.tsx)
The options page provides a settings interface where users can configure your extension. Chrome automatically links to this from the extension management page.
function OptionsPage() {
return (
<div>
<h1>Extension Settings</h1>
{/* Settings form goes here */}
</div>
)
}
export default OptionsPage
New Tab Page (newtab.tsx)
Optionally override Chrome's new tab page with your own custom interface.
Side Panel (sidepanel.tsx)
Create a persistent side panel interface that can stay open while users browse.
Content Scripts: Injecting UI into Web Pages
This is where the real power lies for extensions like ours at TalentSight. Content scripts allow you to inject React components and custom UI elements directly into existing web pages.
Create a content script by adding a file in the contents/ directory:
// contents/linkedin-button.tsx
import type { PlasmoCSConfig } from "plasmo"
export const config: PlasmoCSConfig = {
matches: ["https://www.linkedin.com/*"],
css: ["content.css"]
}
const LinkedInButton = () => {
const handleClick = () => {
// Your custom logic here
console.log("Button clicked!")
}
return (
<button onClick={handleClick} className="custom-button">
Save to Talent Site
</button>
)
}
export default LinkedInButton
Content scripts let you:
- Target specific websites using URL patterns
- Inject custom buttons and UI elements
- Attach to existing page elements
- Execute custom behavior based on page content
This is exactly how we embed TalentSight functionality directly into LinkedIn's interface.
Messaging API: Communicating with Your Backend
Most extensions need to communicate with a backend API to store data, fetch information, or trigger actions. Plasmo's messaging API makes this straightforward.
Setting Up Messaging
First, install the messaging dependency:
npm install @plasmohq/messaging
Creating a Message Handler
Create a message handler in the background script:
// background/messages/api-call.ts
import type { PlasmoMessaging } from "@plasmohq/messaging"
const handler: PlasmoMessaging.MessageHandler = async (req, res) => {
const { body } = req
try {
// Make your API call
const response = await fetch("https://api.yourbackend.com/endpoint", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body)
})
const data = await response.json()
res.send({
success: true,
data: data
})
} catch (error) {
res.send({
success: false,
error: error.message
})
}
}
export default handler
Sending Messages from Extension Pages
Now you can call this handler from any extension page or content script:
import { sendToBackground } from "@plasmohq/messaging"
const saveToBackend = async (userData) => {
const response = await sendToBackground({
name: "api-call", // Must match the handler filename
body: {
user: userData
}
})
if (response.success) {
console.log("Data saved:", response.data)
} else {
console.error("Error:", response.error)
}
}
The standard messaging flow is perfect for one-time requests between extension components. Plasmo also supports relay and ports for more advanced use cases like streaming data or maintaining persistent connections.
Integrating Authentication with Clerk
If your extension connects to a platform where users are already authenticated, you don't want to force them to log in again. Clerk provides seamless authentication integration for Chrome extensions.
For a complete guide on integrating Clerk with your Plasmo extension, including syncing authentication state between your web platform and extension, see the official Clerk documentation for Chrome extensions.
The integration allows you to:
- Automatically authenticate users who are logged into your web platform
- Sync authentication state across browser tabs
- Handle token refresh seamlessly
- Provide a smooth user experience without redundant login flows
Publishing Your Extension
Once your extension is ready, you can publish it to the Chrome Web Store:
- Create a developer account at the Chrome Web Store Developer Dashboard
- Build your production bundle with
npm run build - Zip the contents of your build folder
- Upload to the Chrome Web Store with required metadata, screenshots, and privacy policy
- Submit for review (it can take a while, but after the first submission, next ones will be faster)
Make sure all required manifest fields are properly configured in your package.json before publishing.
Conclusion
Before using Plasmo, our extension was pure HTML,CSS and JavaScript and it was a horrible experience, it didn't match our brand guidelines and was hard to maintain.
Plasmo has transformed how we build browser extensions at TalentSight. What used to require managing complex manifest configurations and wrestling with Chrome's messaging APIs now feels as natural as building a React application.
The key advantages we've experienced:
- Faster development with hot module replacement
- Type-safe messaging between components
- React components work seamlessly
- Significantly less boilerplate code
- Easy integration with modern tools like Clerk
Whether you're building a productivity tool, a LinkedIn integration like ours, or any other browser extension, Plasmo provides the modern developer experience you'd expect from today's frameworks.
Ready to get started? Create your first Plasmo extension and bring your web application directly to where your users work.
Additional Resources
- Plasmo Documentation
- Chrome Extension Documentation
- Clerk Chrome Extension Guide
- Clerk Chrome Extension Sync
- TalentSight Extension - See our extension in action







Top comments (0)