JavaScript SEO Example
A client-rendered page can rank, but only if each route exposes correct signals to crawlers: a unique title, a matching meta description, a self-referencing canonical, and structured data that reflects the visible content. This example shows a small single page application updating all of those on every navigation, the practical core of making JavaScript content crawlable.
For the full picture of how search engines process JavaScript, see JavaScript SEO and how Google crawls JavaScript.
What you'll learn
What this example builds
The example wires per-route metadata into a single page application so that each view sets its own title, description, canonical URL, and JSON-LD before the content shows.
The behaviour you will see
Navigating between routes changes the document title and meta description, repoints the canonical link to the current URL, and swaps the structured data block to match the page. A crawler that renders the page, or a user who shares it, gets signals that describe the actual content rather than a single generic shell.
Updating title and description
The most basic per-page signals are the title and meta description, and both can be set from script.
Setting head tags per route
function setMeta(title, description) {
document.title = title;
var tag = document.querySelector('meta[name="description"]');
if (!tag) {
tag = document.createElement("meta");
tag.setAttribute("name", "description");
document.head.appendChild(tag);
}
tag.setAttribute("content", description);
}
Setting the canonical per route
Each route should declare a canonical that points to its own URL, not the homepage.
Repointing the canonical link
function setCanonical(url) {
var link = document.querySelector('link[rel="canonical"]');
if (!link) {
link = document.createElement("link");
link.setAttribute("rel", "canonical");
document.head.appendChild(link);
}
link.setAttribute("href", url);
}
A correct canonical prevents many routes from collapsing into one in the index, a point covered in SPA indexing.
Updating structured data
Structured data should describe the page currently shown, so it is replaced on navigation.
Swapping the JSON-LD block
function setSchema(obj) {
var el = document.getElementById("ld");
if (!el) {
el = document.createElement("script");
el.type = "application/ld+json";
el.id = "ld";
document.head.appendChild(el);
}
el.textContent = JSON.stringify(obj);
}
Why prerendering still helps
Updating tags in script works, but serving rendered HTML up front is more reliable.
Rendered HTML versus rendered-on-the-client
Search engines can render JavaScript, but rendering is deferred and not guaranteed for every page on every crawl. Sending finished HTML, through server-side rendering or prerendering, means the title, content, and structured data are present in the initial response, with no dependence on the crawler executing your script. The client-side updates in this example then keep everything correct during navigation. The trade-offs are explained in rendering for search engines.
Common pitfalls
JavaScript SEO fails in a few specific ways.
What usually goes wrong
The most common is leaving every route with the same title and description from the static shell, so pages look duplicate. The second is a canonical that points to the homepage on every route, collapsing the site in the index. The third is loading important content only after a user interaction, which a crawler never performs, so it never sees that content. The deeper mechanics are in JavaScript rendering and indexing.
Frequently Asked Questions
How do I set per-page titles in a single page application?
Update document.title on each route change, and create or update the meta description tag the same way. Each route should set signals that describe its own content rather than reusing the static shell's defaults.
Why set a canonical on every route?
Without a per-route canonical, many client-rendered routes can collapse into one URL in the index. Repointing the canonical link to the current route's URL tells search engines each page is distinct.
Is prerendering necessary if Google renders JavaScript?
It is not strictly required but it is more reliable. Rendering is deferred and not guaranteed every crawl, so serving finished HTML puts your title, content, and structured data in the initial response regardless.
Why is my JavaScript content not indexed?
Common causes are duplicate titles across routes, a canonical pointing to the homepage everywhere, or content that loads only after a user interaction a crawler never performs. Each leaves the page looking empty or duplicate.
Read next: the realtime UI example, or back to the Examples hub.
Want the full picture?
The JavaScript SEO cluster covers crawling, rendering, and indexing in depth.
Read: JavaScript SEO →