A Chrome extension that automatically extracts and syncs Contacts, Deals, and Tasks from the ActiveCampaign CRM interface to local storage. includes a React-based popup for viewing extracted data.
- Clone or Download this repository.
- Install Dependencies:
npm install
- Build the Popup UI:
This compiles the React application in
popup/topopup/build/.npm run build
- Load into Chrome:
- Open Chrome and navigate to
chrome://extensions. - Toggle Developer mode in the top right.
- Click Load unpacked.
- Select the root directory of this project (the folder containing
manifest.json).
- Open Chrome and navigate to
- Navigate to your ActiveCampaign dashboard.
- Go to Contacts, Deals, or Tasks pages.
- The extension automatically detects data on the page using a
MutationObserverand extracts it. - You will see a visual feedback notification in the bottom-right corner when data is synced (e.g., "Synced 20 Contacts").
- Click the extension icon to open the popup and view the aggregated data.
The extension uses a robust scraping strategy defined in content.js:
- Uses a MutationObserver to watch
document.bodyfor changes. - This ensures compatibility with ActiveCampaign's Single Page Application (SPA) architecture, triggering extractions dynamically as content loads (hydration).
- A hydration delay (default 1.5s) helps ensure the DOM is stable before reading.
| Entity | Trigger URL | Main Selector | Data Points Scraped |
|---|---|---|---|
| Contacts | /contacts |
tr[data-testid="c-table__row"] |
Name, Email, Phone, Owner |
| Deals | /deals |
[class*="deal-board_column"] |
Deal ID (/deals/123), Title, Value, Stage, Contact Name |
| Tasks | /tasks |
table tbody tr |
Title, Task Type, Related Entity, Assignee, Due Date, Status |
Note: Selectors rely on
data-testidattributes where available (Contacts) for robustness, and class-based/structural selection for others (Deals/Tasks) where stable IDs are absent.
Data is stored locally using chrome.storage.local. The background script (service-worker.js) handles data merging to prevent duplicates, ensuring that existing records are updated rather than overwritten.
ac_contactsac_dealsac_tasks
{
"id": "email@example.com", // Used as unique key
"name": "John Doe",
"email": "email@example.com",
"phone": "+1234567890",
"owner": "Sales Rep Name"
}{
"id": "1001", // Extracted from URL path
"title": "New Deal Opportunity",
"value": "$5,000",
"stage": "To Contact",
"contact": "Jane Smith"
}{
"id": "Call Client-2023-10-01", // Composite key (Title + DueDate)
"title": "Call Client",
"taskType": "Call",
"relatedTo": "Big Corp Deal",
"assignee": "Agent Name",
"dueDate": "Oct 1, 2023",
"status": "In Progress"
}extension/
├── content.js # DOM scraper & observer (injected into ActiveCampaign)
├── service-worker.js # Background service (data persistence & synchronization)
├── manifest.json # Chrome extension configuration
├── popup/ # React-based Popup UI
│ ├── index.html # Entry HTML for popup
│ ├── main.jsx # React entry point
│ ├── App.jsx # Main component & routing logic
│ ├── index.css # Global styles
│ └── components/ # Reusable UI components
│ ├── Header.jsx # Sticky header with logo
│ ├── Tabs.jsx # Navigation tabs (Contacts/Deals/Tasks)
│ ├── DataList.jsx # Data fetching & display logic
│ └── Table.jsx # Reusable table component
├── vite.config.js # Vite build configuration
└── package.json # Dependencies & scripts
content.js: Investigates the DOM of ActiveCampaign pages (*.activehosted.com) to extract CRM data.service-worker.js: Acts as the central event bus; listens for extracted data messages and manages local storage updates.popup/: Contains the source code for the extension's user interface, built with React and Vite.build/: The production output directory generated by Vite (loaded by the extension).