Read the web distraction-free in Raycast.
- Clean Reading Experience - Extracts article content and removes distractions
- AI Summaries - Multiple summary styles powered by Raycast AI
- Browser Extension Fallback - Access blocked pages and re-import member-only content via the Raycast browser extension
- Smart URL Detection - Automatically detects URLs from arguments, clipboard, selection, or active browser tab
| Command | Description | Default |
|---|---|---|
| Open in Reader Mode | Main command - opens a URL in reader mode | Enabled |
| Open Clipboard in Reader Mode | Opens URL from clipboard directly | Disabled |
| Open Current Tab in Reader Mode | Opens the current browser tab in reader mode | Disabled |
The clipboard and current tab commands are disabled by default to reduce command clutter. Enable them in Raycast Settings → Extensions → Reader Mode if you prefer dedicated commands over the main command's auto-detection.
Reader Mode uses a multi-layered approach to extract clean article content:
- Site-Specific Extractors (
src/extractors/) - Custom extraction logic for complex sites - Site Configuration (
src/config/site-config.ts) - Selector-based configuration for simpler sites - Mozilla Readability - Fallback for all other sites
| Approach | Use Case | Examples |
|---|---|---|
| Extractors | Sites needing custom DOM traversal and content transformation | Hacker News, GitHub, Reddit |
| Site Config | Sites needing only CSS selector adjustments | Medium, Substack, news sites |
| Readability | Standard article pages | Most blogs and news articles |
For simple sites (just need different selectors), add to src/config/site-config.ts:
[
/^example\.com$/i,
{
name: "Example",
articleSelector: ".article-body",
removeSelectors: [".ads", ".sidebar"],
},
],For complex sites (need custom extraction logic), create a new extractor:
- Create
src/extractors/mysite.tsextendingBaseExtractor - Implement
canExtract(),extract(), andget siteName() - Register in
src/extractors/index.ts
export class MySiteExtractor extends BaseExtractor {
get siteName(): string {
return "My Site";
}
canExtract(): boolean {
return !!this.querySelector(".my-content");
}
extract(): ExtractorResult {
// Custom extraction logic
return { content, textContent, metadata };
}
}Reader extracts content from live web pages, whose markup — and whose paywalls — change constantly. To keep that brittle logic honest, the repo ships an automated test suite:
npm testIt covers content extraction, HTML cleaning, and paywall detection, including the specific regressions these features have hit before. The committed fixtures in tests/fixtures/ are synthetic pages that reproduce real paywall structure without copying any publisher's content, so the suite runs on a fresh clone with no extra setup. If you change extraction, cleaning, or paywall detection, please add a case — see CONTRIBUTING.md.
The extension uses a modular configuration system located in src/config/:
Controls which AI model and creativity level is used for each summary style. This allows fine-tuning performance per summary type.
export const AI_SUMMARY_CONFIG: Record<SummaryStyle, AIStyleConfig> = {
overview: { model: AI.Model["OpenAI_GPT-5_nano"], creativity: "low" },
"opposite-sides": { model: AI.Model["OpenAI_GPT-5_nano"], creativity: "low" },
// ...
};Contains all summary prompt templates in one place for easy comparison and editing.
export const SUMMARY_PROMPTS: Record<SummaryStyle, PromptConfig> = {
overview: {
label: "Overview",
buildPrompt: (context) => `${context}\n\nSummarize this article...`,
},
// ...
};Each prompt config includes:
label- Human-readable name shown in the UIbuildPrompt- Function that generates the full prompt from article context
| Style | Description |
|---|---|
| Overview | One-liner summary + 3 key bullet points |
| At a Glance | Summary + Key Takeaways in a concise format |
| Comprehensive | Fact-filled bullet points from the author's POV |
| Opposing Sides | Two contrasting viewpoints from the article |
| The 5 Ws | Who, What, Where, When, Why breakdown |
| Explain Like I'm 5 | Simplified explanation using simple language |
| People, Places & Things | Key entities extracted with context |
All summary styles can be generated in your preferred language. Set the Summary Output Language preference to choose from 21 supported languages including English (default), Spanish, French, German, Japanese, Chinese, and more. When a non-English language is selected, summaries will be generated in that language regardless of the article's original language.
Reader Mode integrates with the Raycast browser extension to handle blocked pages and access authenticated content.
Some websites (like Politico, Bloomberg, etc.) use bot detection that prevents direct content fetching. When this happens, Reader Mode automatically offers a browser extension fallback:
How It Works:
- Detection - When a 403 "Access Denied" error occurs, Reader Mode checks if you have the Raycast browser extension installed
- Instructions - Shows a friendly message with clear steps to access the content
- Browser Fallback - You can open the page in your browser and fetch content via the extension
Usage:
- Press Enter to open the URL in your browser
- Wait for the page to fully load
- Press ⌘ + R to fetch the content via the Raycast browser extension
- The article loads normally with full content
For paywalled or member-only articles (like Medium member stories), you can re-import content from an authenticated browser session:
When to Use:
- Medium member-only articles
- Paywalled content from news sites
- Any article requiring authentication to view full content
How It Works:
- Open the article in Reader Mode (you'll see a truncated or blocked version)
- Press ⌘ + ⇧ + R to trigger "Import from Browser Tab"
- Reader Mode finds the matching browser tab using the article's canonical URL
- If the tab is inactive, you'll be prompted to focus it first
- Content is re-imported with your authenticated session, showing the full article
Requirements:
- Raycast browser extension must be installed
- The article must be open in a browser tab
- You must be logged in to the site in your browser
- The browser tab must be active (focused) when importing
This extension's content extraction architecture was inspired by Defuddle, a content extraction library by @kepano. We initially attempted to use Defuddle directly, but found it wasn't well-suited for Raycast's environment:
- DOM Environment: Defuddle expects a browser DOM, while Raycast extensions run in Node.js with
linkedom - Bundle Size: Defuddle's full feature set added unnecessary weight for our use case
- Output Format: We needed tighter integration with our metadata extraction and markdown conversion pipeline
Instead, we adopted Defuddle's excellent patterns:
- Site-specific extractors with a clean base class architecture
- Schema.org JSON-LD parsing for rich metadata extraction
- Fallback chains for metadata (Schema.org → Open Graph → Twitter Cards → meta tags)
- Comprehensive cleanup selectors for removing ads, navigation, and other distractions
This hybrid approach gives us the best of both worlds: Defuddle's battle-tested extraction patterns with tight Raycast integration.
Square brackets [text] that appear in article content (such as editorial insertions in quotes) are automatically converted to parentheses (text) to prevent Raycast's markdown renderer from interpreting them as LaTeX math notation. This is a workaround for a rendering limitation and means the displayed text may differ slightly from the original source material.
Image alt text and title attributes are automatically stripped to ensure proper rendering in Raycast. Images are displayed as  without descriptive text. This prevents rendering issues where long alt text or title attributes (especially those containing quotes) can break the markdown image syntax.
Additionally, relative image URLs (e.g., /image.jpg) are automatically converted to absolute URLs using the page's base URL to ensure images load properly.
Paywall Detection: Externally-Hidden Barriers
Paywall detection runs on the fetched page HTML only — it has no network access while scoring, so it cannot read a page's external stylesheets. It accounts for barrier markup hidden by the hidden/aria-hidden attributes, inline styles, inert containers (<template>, etc.), and the page's own inline <style> rules. It cannot tell that a barrier element (e.g. class="article-gate") is hidden by a rule in a linked stylesheet.
So a readable article that also ships an inactive, externally-hidden paywall template can occasionally be misclassified as paywalled and sent through the Paywall Hopper bypass. The impact is bounded: retrieved content only replaces the original when it is at least 20% longer (see article-loader.ts), and an already-complete article is unlikely to gain that much from an archive — so the practical cost is a redundant bypass attempt, not swapped-out content. Fixing it properly requires evaluating linked CSS, which is out of scope for the detector's static, no-network design. This is a deliberate, accepted trade-off; see docs/known-issues.md for the full rationale.
- Mozilla Readability - Core content extraction
- Defuddle - Inspiration for extractor architecture
- Turndown - HTML to Markdown conversion
- Raycast API Docs
- Logger Integration Guide
- Extension Spec