03 / Content
Dynamic content is any content added or changed by JavaScript after the initial HTML loads: product details fetched from an API, reviews injected by a widget, text inside tabs, or items that load as the user scrolls. Search engines can index much of it, but only when it is present in the rendered page without user interaction.
This lesson covers API-driven content, lazy loading, user interactions, JavaScript that modifies SEO elements, structured data injection, and personalization, with checks for each.
1. Types of dynamic content
| Type | Example | Indexing risk |
|---|---|---|
| API-rendered content | Product description fetched after load | Medium. Depends on the API responding in time |
| Lazy-loaded content | Images or sections loaded when scrolled into view | Low to high. Depends on the loading method |
| Interaction-gated content | Text revealed only after clicking a tab or button | High if the content is not in the DOM until clicked |
| Third-party widgets | Reviews, comments, related products | Medium. Often loaded in iframes or late scripts |
| Modified SEO elements | Title, meta robots, canonical changed by JavaScript | High when raw and rendered values conflict |
| Personalized content | Location, login, or A/B test variations | Medium. Googlebot sees one version |
2. Content behind user interactions
The renderer loads the page but does not click, type, hover, or scroll like a user. The key question is whether content exists in the DOM before the interaction.
Tabs and accordions
Content hidden with CSS is still indexable. Content that does not exist until a click is not.
3. Lazy loading
Lazy loading delays offscreen resources to improve performance. It is safe when done with native browser features or observers that trigger in the renderer.
| Method | Crawler friendly? | Notes |
|---|---|---|
loading="lazy" on img and iframe | Yes | Native, and the src remains in the HTML |
| IntersectionObserver | Usually | Google's renderer uses a tall viewport, so content often loads |
| Scroll event listeners | Risky | The renderer does not scroll like a user |
| Image URL in data-src only | Risky | Without a fallback, the image URL may never be seen |
Never lazy load the main hero image or above the fold content. It delays Largest Contentful Paint and gains nothing. The Images lesson covers image attributes in more detail.
4. JavaScript that changes SEO elements
Scripts often update the title, meta description, canonical, or meta robots tag after load. Google generally uses the rendered values, but conflicts create risk.
Raw noindex removed by JavaScript
Google may skip rendering when the raw HTML says noindex, so the page stays out of the index.
Canonical changed after render
Two different canonical signals weaken consolidation.
Generic raw title
A placeholder such as Loading or the site name appears in snippets or AI answers.
Status code masking
A soft 404 page returns 200 with a not found message rendered by JavaScript.
Title, meta robots, canonical, hreflang, and status codes should be correct in the server response. JavaScript can enhance a page, but it should not be responsible for telling search engines whether to index it.
5. Soft 404s in single-page apps
Client-side apps often return HTTP 200 for every route, then render a not found message when data is missing. Search engines may treat these as soft 404s or, worse, index empty pages. Fix this by returning a real 404 from the server, or by redirecting to a URL that does, and by adding a noindex in the raw HTML for error views. See the Status codes lesson.
6. Injected structured data
JSON-LD added with JavaScript or a tag manager can be read by Google after rendering, but it depends on successful rendering and is invisible to crawlers that do not run scripts. Product, Article, and FAQ schema is safest in the server response. Validate injected markup against the rendered page, not the source. Related issues: schema missing and schema invalid. The article on schema markup for AI explains its role in AI visibility.
7. Personalization, geolocation, and A/B tests
Googlebot crawls mostly from US IP addresses, without cookies, and without a logged-in session. Content that depends on location, login state, or a test variant may differ from what users see. Keep the core content and links the same for every visitor, avoid redirecting based on IP, and make sure test variants do not change canonical tags or block indexing.
8. Auditing dynamic content in SiteAuditLint
- Pick a unique phraseChoose a sentence from content loaded by JavaScript, such as a product description or review.
- Search the raw HTMLCheck whether the phrase exists in the initial response.
- Search the rendered HTMLRun a rendered crawl and confirm the phrase appears.
- Compare SEO elementsFlag titles, canonicals, and meta robots that differ between versions.
- Check thin rendered pagesPages with very little text after rendering may be failing to load data.
SiteAuditLint's custom source search can check every crawled URL for a phrase, element, or pattern, which makes this comparison fast across a whole site. Pages that render empty often appear under thin content.
9. Practical exercise: Test three dynamic elements
Choose three pieces of dynamic content on your site, such as reviews, tab content, and a product description, and test each one in the raw and rendered HTML.
Dynamic content checklist
0 of 8 tasks completed
Key takeaways
- Dynamic content is indexable when it exists in the rendered DOM without interaction.
- Content fetched only on click or scroll is at risk.
- Native lazy loading is crawler friendly. Scroll listeners are not.
- Critical signals such as meta robots and canonical belong in the raw HTML.
- Single-page apps need real 404 status codes to avoid soft 404s.
- Server-rendered structured data is more reliable than injected schema.
Knowledge check
Next, bring rendering, links, and dynamic content together in the Indexing lesson.