JavaScript SEO Guide: How to Make JS Sites Search-Friendly
Complete guide to JavaScript SEO. Learn how search engines render JavaScript, common issues, and how to ensure your JS-heavy site gets indexed properly.
JavaScript powers modern web applications, but it creates unique challenges for SEO. Search engines must render JavaScript to see your content—and they don’t always do it perfectly.
This guide covers everything you need to know about JavaScript SEO.
How Search Engines Process JavaScript
Google processes JavaScript pages in two waves:
Wave 1: Initial HTML Parsing
- Googlebot crawls the URL
- Parses the initial HTML response
- Indexes basic content (if any exists in HTML)
- Discovers links in HTML
- Adds page to render queue
Wave 2: Rendering
- Page enters render queue
- Googlebot renders JavaScript using Chrome
- Indexes additional content discovered
- Discovers links added by JavaScript
The gap between waves can be days or weeks. This delay is why JavaScript SEO is challenging.
Common JavaScript SEO Problems
1. Content Not in Initial HTML
Problem: Content only appears after JavaScript executes.
<!-- What Google sees initially -->
<div id="app"></div>
<!-- What Google needs to see -->
<div id="app">
<h1>Your Page Title</h1>
<p>Your actual content...</p>
</div>
Impact: Delayed indexing, possibly incomplete indexing.
2. Links Not Crawlable
Problem: JavaScript-powered links that Google can’t follow.
// NOT CRAWLABLE
<div onclick="goToPage('/product')">View Product</div>
// NOT CRAWLABLE - no href
<a onclick="navigate('/product')">View Product</a>
// CRAWLABLE - proper anchor tag
<a href="/product">View Product</a>
Impact: Internal pages won’t be discovered or indexed.
3. Blocked JavaScript Resources
Problem: CSS or JS files blocked by robots.txt.
# DON'T DO THIS
User-agent: *
Disallow: /js/
Disallow: /css/
Impact: Google can’t render your page properly.
4. JavaScript Errors
Problem: JavaScript errors prevent content from rendering.
Impact: Content never appears for Google to index.
5. Lazy Loading Issues
Problem: Content loaded on scroll/interaction never renders.
// Google won't see this content
document.addEventListener('scroll', loadMoreContent);
// Or click-to-load
button.addEventListener('click', loadContent);
Impact: Below-fold content not indexed.
6. Client-Side Routing Issues
Problem: SPAs with client-side routing that don’t work without JavaScript.
// URL changes but server returns same HTML
history.pushState({}, '', '/new-page');
Impact: Direct URL access fails, poor crawlability.
Testing JavaScript SEO
Method 1: Google Search Console URL Inspection
- Enter URL in Search Console
- Click “Test Live URL”
- View “Tested page” > “View tested page”
- Compare HTML to rendered HTML
Method 2: Site Search
site:yourdomain.com "specific content text"
If Google can’t find content that exists on your page, it’s not being indexed.
Method 3: Mobile-Friendly Test
- Enter URL in Google’s Mobile-Friendly Test
- View rendered page screenshot
- Check rendered HTML for your content
Method 4: Disable JavaScript
- Disable JavaScript in browser DevTools
- Visit your pages
- Check if content/links are visible
If nothing appears, neither will Google see anything in the first pass.
Solutions for JavaScript SEO
Option 1: Server-Side Rendering (SSR)
How it works: Server renders JavaScript and sends complete HTML.
Best for: React (Next.js), Vue (Nuxt), Angular (Angular Universal)
Pros:
- Full content in initial HTML
- Fast first paint
- No indexing delay
- Works without JavaScript
Cons:
- Server load increases
- More complex deployment
- TTFB may increase
Example (Next.js):
// pages/product/[id].js
export async function getServerSideProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
export default function ProductPage({ product }) {
return <ProductDetails product={product} />;
}
Option 2: Static Site Generation (SSG)
How it works: Pages pre-rendered at build time.
Best for: Content that doesn’t change frequently.
Pros:
- Fastest performance
- Perfect for SEO
- Low server cost
- CDN-friendly
Cons:
- Build time for many pages
- Not suitable for dynamic content
- Requires rebuild for updates
Example (Next.js):
export async function getStaticProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
export async function getStaticPaths() {
const products = await fetchAllProducts();
return {
paths: products.map(p => ({ params: { id: p.id } })),
fallback: 'blocking',
};
}
Option 3: Hybrid Rendering
How it works: Combine SSR, SSG, and client-side based on page needs.
Best for: Complex sites with varying needs.
Example:
- Product pages: SSG with ISR (Incremental Static Regeneration)
- Search results: Client-side
- Blog posts: SSG
- User account: Client-side (noindex anyway)
Option 4: Dynamic Rendering
How it works: Serve pre-rendered HTML to bots, JavaScript to users.
Best for: Sites that can’t implement SSR.
Tools: Prerender.io, Puppeteer, Rendertron
How it works:
- Detect if request is from a bot (user-agent)
- If bot: serve pre-rendered static HTML
- If user: serve normal JavaScript app
Important: Google allows this, but it must render the same content for both.
Option 5: Progressive Enhancement
How it works: Build with HTML/CSS first, enhance with JavaScript.
Best for: Content-focused sites.
Approach:
- Core content works without JavaScript
- JavaScript adds interactivity
- Everything degrades gracefully
JavaScript SEO Best Practices
1. Use Proper Link Markup
<!-- CORRECT -->
<a href="/page">Link Text</a>
<!-- WRONG -->
<span onclick="goToPage('/page')">Link Text</span>
<a onclick="navigate('/page')">Link Text</a>
<button onclick="location='/page'">Link Text</button>
2. Implement History API Correctly
For SPAs with client-side routing:
// Push state changes URL
history.pushState({ page: 'about' }, '', '/about');
// But server must also handle /about directly!
Ensure your server returns appropriate HTML for any URL.
3. Handle Meta Tags Dynamically
Update title and meta descriptions for each page:
// React Helmet example
import { Helmet } from 'react-helmet';
function ProductPage({ product }) {
return (
<>
<Helmet>
<title>{product.name} | Your Site</title>
<meta name="description" content={product.description} />
</Helmet>
<ProductDetails product={product} />
</>
);
}
4. Use Canonical Tags
Set canonical URLs for JavaScript-generated pages:
<Helmet>
<link rel="canonical" href={`https://example.com${router.pathname}`} />
</Helmet>
5. Avoid Lazy Loading Critical Content
Above-fold content should load immediately:
// Critical content - load immediately
<div>{mainContent}</div>
// Below-fold content - can lazy load
<LazyComponent onVisible={() => loadMoreContent()} />
6. Ensure Resources Aren’t Blocked
Check robots.txt allows JS/CSS:
User-agent: *
Allow: /js/
Allow: /css/
Allow: /_next/ # Next.js
Allow: /static/
7. Handle Errors Gracefully
Don’t let JavaScript errors break the page:
// Error boundary (React)
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
8. Implement Structured Data
Add JSON-LD in a way that’s present in rendered HTML:
// Add to head or body
<script type="application/ld+json">
{JSON.stringify({
"@context": "https://schema.org",
"@type": "Product",
"name": product.name,
"description": product.description,
})}
</script>
JavaScript Framework Specific Tips
React
- Use Next.js for SSR/SSG
- Implement react-helmet for meta tags
- Use proper anchor tags for navigation
Vue
- Use Nuxt for SSR/SSG
- Implement vue-meta for meta tags
- Use router-link with href
Angular
- Use Angular Universal for SSR
- Implement meta service for tags
- Ensure proper href attributes
Vanilla JavaScript
- Consider pre-rendering
- Use progressive enhancement
- Ensure HTML contains core content
JavaScript SEO Checklist
- Core content visible in source HTML
- All links use proper
<a href="">tags - Meta tags update per page
- Canonical tags implemented
- JavaScript resources not blocked
- No console errors on key pages
- Above-fold content loads immediately
- Server handles direct URL requests
- Structured data renders properly
- Mobile-Friendly Test shows full content
Related Resources:
Tags:
Share this article:
Ready to improve your SEO?
Get a free SEO audit and see exactly what needs fixing on your site
Start Free Audit