https://www.loom.com/share/38832d4660e44e34bc7862d5b41ec258
As someone who reads online most of the time, I find the current reading flow annoying and requires too much switching. When I read, I want to engage AND save information so that I can revisit it later. Now, all of my data lives in Notion, and whenever I read a research paper or something that requires serious attention, I need to open a split view, copy from one tab, paste it into Notion, and open another tab, like Claude or ChatGPT, to ask follow-up questions. I was thinking about how I can automate this process so that my online reading experience is more seamless while ensuring the right information is saved to my knowledge base. And this is why inline was born :)
Estimated Time Spent: ~15-20 mins
Before you begin, ensure you have the following installed:
- Node.js (v18 or higher) - Download
- npm (comes with Node.js)
- Chrome browser (for loading the extension)
- Notion API key - Get one here
- Go to https://www.notion.so/my-integrations
- Click "+ New integration"
- Give it a name (e.g., "Inline Extension")
- Select the workspace where you want to save content
- Click "Submit" to create the integration
- Copy the "Internal Integration Token" - you'll need this for the
NOTION_KEYenvironment variable - Important: Share the pages/databases you want to use with this integration:
- Open the Notion page or database you want to use
- Click the "..." menu in the top right
- Select "Add connections" or "Connections"
- Find and select your integration
git clone <repository-url>
cd inline-
Navigate to the server directory:
cd server -
Install dependencies:
npm install
-
Create a
.envfile in theserverdirectory:touch .env
-
Add your Notion API key to the
.envfile:NOTION_KEY=your_notion_api_key_here PORT=64707
Note: The default port is
3000, but the frontend expects64707. You can change the port in the frontend'sapi.tsfile if needed. -
Build the server:
npm run build
-
Start the server:
# For development (with auto-reload): npm run dev # For production: npm start
The server should now be running on
http://localhost:64707(or the port you specified).
-
Navigate to the frontend directory:
cd ../frontend -
Install dependencies:
npm install
-
Build the extension:
npm run build
This will create a
distfolder with the compiled extension files. -
Optional: If you need to change the server URL (if your server runs on a different port or host), edit
frontend/src/api.ts:export const API_BASE = "http://localhost:64707"; // Change this to your server URL
Then rebuild:
npm run build
- Open Chrome and navigate to
chrome://extensions/ - Enable "Developer mode" (toggle in the top right)
- Click "Load unpacked"
- Select the
frontend/distfolder from the project directory - The extension should now appear in your extensions list
- Make sure the server is running (you should see it listening on the configured port)
- Click the extension icon in Chrome to open the side panel
- Try searching for a Notion page or database
- If everything is set up correctly, you should see your Notion pages/databases in the search results
For development with hot-reload:
Server:
cd server
npm run devNote: After making changes to the frontend, you'll need to rebuild (npm run build) and reload the extension in Chrome for changes to take effect.
Inline is a Chrome extension that enables seamless saving of web content to Notion. The architecture consists of three main components:
- Chrome Extension (Frontend) - React-based UI and content scripts that interact with web pages
- Express.js Server (Backend) - API server that handles Notion API interactions
- Notion API - External service for reading and writing data
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Chrome Browser β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Content Script (content.ts) β β
β β - Detects text selection β β
β β - Shows tooltip with Save/Comment buttons β β
β ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β chrome.runtime.sendMessage β
β ββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ β
β β Service Worker (sw.ts) β β
β β - Message routing β β
β β - API calls to backend β β
β β - Storage management β β
β ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β HTTP requests β
β ββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ β
β β Side Panel (popup/App.tsx) β β
β β - React UI for destination selection β β
β β - Search and browse Notion pages/databases β β
β β - Property form for database entries β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
β HTTP (REST API)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Express.js Server (server.ts) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Routes: β β
β β - POST /search - Search Notion β β
β β - GET /children/:id - Get child pages β β
β β - GET /data-sources/:id - Get data sources β β
β β - POST /create-page - Create new page β β
β β - PATCH /save - Save content to page β β
β β - POST /save-with-comment - Save + comment β β
β β - POST /comment - Add comment to block β β
β ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββ β
β β @notionhq/client β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Notion API β
βββββββββββββββββββ
The extension is built with React, TypeScript, and Vite, and consists of three main parts:
- Purpose: Injected into every webpage to detect user interactions
- Key Features:
- Monitors text selection events (
mouseup,selectionchange) - Displays tooltip with "Save" and "Comment" buttons when text is selected
- Highlights selected text with optimistic UI updates
- Manages image hover detection for saving images
- Monitors text selection events (
- Communication: Sends messages to service worker via
chrome.runtime.sendMessage()
- Purpose: Background script that handles extension logic and API communication
- Key Features:
- Routes messages from content script to appropriate handlers
- Makes HTTP requests to the Express.js backend
- Manages Chrome storage for destination persistence
- Handles extension lifecycle (opens side panel on icon click)
- Implements retry logic and timeout handling
- Purpose: React-based UI for managing Notion destinations
- Key Features:
- Search interface for finding Notion pages, databases, and data sources
- Nested hierarchy display (page β database β data source)
- Destination selection and persistence
- Property form for database entries (supports text, number, select, date, url types)
- Create child pages directly from the UI
Built with Express.js and TypeScript, the server acts as a proxy between the extension and Notion API.
POST /search- Searches Notion for pages and data sources matching a queryGET /children/:pageId- Retrieves child pages of a given pageGET /data-sources/:databaseId- Retrieves data sources connected to a databaseGET /data-source/:dataSourceId- Gets details of a specific data sourcePOST /create-page- Creates a new Notion page with optional propertiesPATCH /save- Appends content (text as quote blocks + images) to a Notion pagePOST /save-with-comment- Combined endpoint that saves content and adds a comment in one requestPOST /comment- Adds a comment to a specific block
notionClient.ts- Initializes and exports the Notion API clientimageUpload.ts- Handles image uploads to Notion (converts images to base64 and creates image blocks)
- User selects text on a webpage
- Content script detects selection and shows tooltip
- User clicks "Save" button
- Content script sends
SAVE_HIGHLIGHTmessage to service worker - Service worker calls
PATCH /saveendpoint with page ID and content - Server creates quote block in Notion page
- Response flows back through the chain
- Optimistic UI update shows success immediately
- User selects text and clicks "Comment"
- Content script shows comment input box
- User enters comment and submits
- Content script sends
COMMENT_HIGHLIGHTmessage - Service worker calls
POST /save-with-comment(or separate/save+/commentcalls) - Server saves content and adds comment thread
- Success feedback shown to user
- User opens side panel
- User searches for Notion page/database
- Frontend calls
POST /searchendpoint - Server queries Notion API and returns results
- User selects a destination
- Destination saved to
chrome.storage.local - Content script listens for storage changes and updates UI visibility
- React 19 - UI framework
- TypeScript - Type safety
- Vite - Build tool and dev server
- Tailwind CSS v4 - Styling
- Chrome Extension APIs -
chrome.storage,chrome.runtime,chrome.sidePanel
- Express.js - Web framework
- TypeScript - Type safety
- @notionhq/client - Official Notion API client
- CORS - Cross-origin resource sharing
- dotenv - Environment variable management
The extension shows success feedback immediately while processing in the background, improving perceived performance.
The /save-with-comment endpoint reduces API calls from 2 to 1, improving speed when adding comments.
The tooltip is injected into a Shadow DOM to avoid CSS conflicts with host pages.
Checks if extension context is still valid to handle cases where the extension was reloaded, preventing stale content script errors.
Service worker implements retry logic for API calls to handle transient network issues and service worker wake-up delays.
inline/
βββ frontend/
β βββ src/
β β βββ cs/
β β β βββ content.ts # Content script
β β βββ sw/
β β β βββ sw.ts # Service worker
β β βββ popup/
β β β βββ App.tsx # Main React component
β β β βββ components/ # UI components
β β β βββ hooks/ # Custom React hooks
β β βββ api.ts # API client
β β βββ types.ts # TypeScript types
β βββ public/
β β βββ manifest.json # Extension manifest
β βββ dist/ # Built extension files
β
βββ server/
βββ routes/ # API route handlers
βββ services/ # Business logic
β βββ notionClient.ts
β βββ imageUpload.ts
βββ helpers/ # Utility functions
βββ server.ts # Express app entry point
- Add a cache feature to show the latest destinations so users don't have to search all over again
- Add Gemini API for an "Ask" mode where users can ask questions about specific text directly in the tab without switching
- Add voice mode for adding comment