Serving the same content to humans, search engines, and AI crawlers the right way.
When we build websites, we think about human users first design, images, interactions, and layout. But today, the web is read by more than just humans:
- Search engines crawl pages to index and rank them
- AI crawlers read content to understand, summarize, and reuse it
The problem is that humans, search engines, and AI crawlers do not read the web the same way.
A typical web page contains images, buttons, layout containers, and JavaScript-driven interactions. This works great for humans, but machines care about content clarity, not UI.
- Search engines must parse unnecessary UI elements before reaching core text
- Important information may be hidden behind JavaScript interactions
- AI crawlers struggle to extract clean, structured information
- Educational content, tutorials, and Q&A become harder to interpretpret
The content exists, but machines don't read it efficiently.
Indexable solves this by separating content from presentation. It automatically extracts semantic content from your rendered UI and injects it as hidden, crawlable markup.
- Humans → Rich UI (images, buttons, interactions)
- Search Engines → Clean, fast, text-focused HTML
- AI Crawlers → Structured Markdown
As long as the meaning and data remain the same, this approach is SEO-safe and future-proof.
- Automatic Extraction: Identifies and preserves meaningful content (headings, paragraphs, lists, code blocks)
- UI Noise Removal: Strips away interactive elements (buttons, forms, navigation)
- Image Handling: Converts images to text representations using alt attributes
- Deterministic: No AI, no guessing pure, predictable content transformation
- SEO Safe: Hidden with CSS only (
display: none), no cloaking, no user-agent detection - Zero Configuration: Simple API with sensible defaults
npm install react-indexableLet's take a simple math question page:
import { Indexable } from 'react-indexable';
export default function MathQuestion() {
const [showAnswer, setShowAnswer] = useState(false);
return (
>gt;
main id="content">gt;
h1>gt;Math Questionh1>gt;
img src="math.png" alt="Math illustration" />gt;
p>gt;strong>gt;Question:strong>gt; What is 8 + 4?p>gt;
button onClick={() =>gt; setShowAnswer(true)}>gt;Show Answerbutton>gt;
{showAnswer &∓& p>gt;Answer: 12p>gt;}
main>gt;
Indexable source="#content" />gt;
>gt;
);
} Math Question
Question: What is 8 + 4?
{showAnswer &&Answer: 12
}What humans see:
- Rich UI with images and interactive button
What gets extracted for crawlers:
# Math Question
[Image: Math illustration]
**Question:** What is 8 + 4?
Answer: 1212The button is automatically removed. Images are converted to text. Only semantic content remains.
import { Indexable } from 'react-indexable';
function Hero() {
return (
section className="hero-section">gt;
h1>gt;Product Nameh1>gt;
p>gt;Revolutionary solution for modern web development.p>gt;
button onClick={handleClick}>gt;Get Startedbutton>gt;
img src="hero.jpg" alt="Product dashboard screenshot" />gt;
section>gt;
);
}
function Features() {
return (
section className="features-grid">gt;
h2>gt;Key Featuresh2>gt;
div className="feature-cards">gt;
div className="card">gt;
img src="icon1.svg" alt="Performance icon" />gt;
h3>gt;Fast Performanceh3>gt;
p>gt;Optimized for speedp>gt;
div>gt;
div className="card">gt;
img src="icon2.svg" alt="Integration icon" />gt;
h3>gt;Easy Integrationh3>gt;
p>gt;Works with existing toolsp>gt;
div>gt;
div>gt;
section>gt;
);
}
export default function LandingPage() {
return (
>gt;
main id="main-content">gt;
Hero />gt;
Features />gt;
main>gt;
Indexable source="#main-content" />gt;
>gt;
);
} Product Name
Revolutionary solution for modern web development.
);
}
function Features() {
return (
Key Features
Fast Performance
Optimized for speed
Easy Integration
Works with existing tools
Extracted Markdown (what crawlers see):
# Product Name
Revolutionary solution for modern web development.
[Image: Product dashboard screenshot]
## Key Features
[Image: Performance icon]
### Fast Performance
Optimized for speed
[Image: Integration icon]
### Easy Integration
Works with existing toolslsAll CSS classes, interactive elements, wrapper divs, and layout containers are removed. Only semantic content remains.
The same factual content is served to everyone. Only the presentation differs.
Search engines immediately see clean content without parsing UI elements.
AI systems get structured Markdown without HTML noise.
- No user-agent detection
- No cloaking (content hidden with CSS, not conditionally rendered)
- No content generation or modification
- Follows search engine guidelines
The primary component for content extraction.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
source |
string |
Yes | - | CSS selector of the content container to extract from |
enabled |
boolean |
No | true |
Toggle extraction on/off |
onExtract |
(markdown: string) => voidoid |
No | - | Callback function that receives the extracted markdown |
Indexable
source="#content"
enabled={true}
onExtract={(markdown) =>gt; {
console.log('Extracted:', markdown);
}}
/>gt; Indexable follows a four-step process to transform UI-heavy content into clean, crawlable markup:
Clones the specified content container from the DOM without mutating the original. Your interactive UI remains untouched.
const cloned = sourceElement.cloneNode(true);Identifies and preserves only meaningful content:
Keeps:
- Headings:
h1,h2,h3,h4,h5,h6 - Text:
p,strong,em,blockquote - Lists:
ul,ol,li - Code:
pre,code - Links:
a - Tables:
table,thead,tbody,tr,th,td
Removes:
- Interactive:
button,form,input,select,textarea - Navigation:
nav,header,footer - Media:
svg,canvas,video,audio - Scripts:
script,style - Layout: All wrapper
divandspanelements
Converts:
- Images to
[Image: alt text]format
Strips:
- All HTML attributes (class, style, id, etc.)
Converts cleaned HTML to Markdown using Turndown with deterministic rules. No AI involved just pure transformation.
4. Hidden Injection
Injects the markdown into a hidden container that's visible to crawlers but not to users:
div data-indexable style="display:none" aria-hidden="true">gt;
article>gt;
pre>gt;-->pre>gt;
article>gt;
div>gt;