Search engines
One component in the head tells search engines, Google Scholar, and link previews what each page is. A sitemap tells them which pages exist.
Experimental May change in any minor version before 1.0.
In this chapter 8 pages
Put Seo in the head
<Seo> takes the place of your own <title>. Set site in astro.config.mjs first: the canonical link and the preview addresses must be absolute, and the component leaves them out without it.
---
import "@dario-loi/polytope/css";
import { Seo } from "@dario-loi/polytope/astro";
import og from "../assets/og.png";
const { title, description } = Astro.props;
---
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<Seo
title={`${title} - John Doe`}
description={description}
image={og}
imageAlt="John Doe's name over a random complex"
siteName="John Doe"
sitemap="/sitemap-index.xml"
/>
</head>
...From these props it writes:
- the title and the description a search result shows;
- a canonical link to the page's own address, so a copy at another address does not compete with it;
- Open Graph and Twitter tags, which messaging apps and social sites read to draw a link preview with the title, the description, and the image;
- schema.org records as JSON-LD, for the person, the paper, or the trail of pages;
- a
noindexrule, for pages that should stay out of results.
Give every page its own title and description. Two pages with the same title look like one page twice in a list of results.
A preview image
Link previews use an image of 1200 by 630 pixels. Keep the important part in the middle: some apps crop it to a square. An image imported from src/assets carries its width and height, which the component passes on, so a preview can be drawn before the image arrives. A path such as /og.png in public/ works too; add width and height yourself: image={{ src: "/og.png", width: 1200, height: 630 }}.
A sitemap
A sitemap lists every page, so a search engine finds pages that few links point to. Astro's own integration writes one at each build:
npx astro add sitemapIt writes sitemap-index.xml at the root of your site, under your base. Point to it with the sitemap prop, and from robots.txt, which an endpoint can write from the same site:
import type { APIRoute } from "astro";
export const GET: APIRoute = ({ site }) =>
new Response(`User-agent: *\nAllow: /\n\nSitemap: ${new URL("sitemap-index.xml", site)}\n`);Homepages and CVs
On the page about you, person adds a Person record. Its sameAs list ties the page to your profiles elsewhere, so a search engine can tell that the ORCID, the Scholar profile, and the homepage belong to the same person and not to someone with the same name.
<Seo
title="John Doe"
description="PhD student at Example University, working on graph learning."
type="profile"
person={{
name: "John Doe",
jobTitle: "PhD student",
affiliation: "Example University",
sameAs: [
"https://orcid.org/0000-0000-0000-0000",
"https://scholar.google.com/citations?user=...",
],
}}
/>Paper pages
Google Scholar finds papers by the citation_ tags in the head of the page. citation writes them, along with a ScholarlyArticle record; reference managers such as Zotero read the same tags when a reader saves the page. The authors take the same shape as PaperHeader's, so one array feeds both.
---
import { Seo, PaperHeader } from "@dario-loi/polytope/astro";
const authors = [{ name: "A. Author", affiliations: [1] }, { name: "John Doe", affiliations: [2] }];
const paper = {
title: "Example title: Higher-order message passing on cell complexes",
authors,
date: "2026-05-01",
conference: "Example Venue 2026",
pdf: "paper.pdf",
};
---
<head>
<Seo title={paper.title} description="One or two sentences from the abstract." citation={paper} />
</head>
<body>
<PaperHeader title={paper.title} authors={authors} venue={paper.conference} />
...
</body>Scholar checks the tags against the page itself, so keep the title and the authors on the page as PaperHeader prints them, keep the abstract on the page, and give each paper a page of its own. A PDF on the same site, named by pdf, is the copy Scholar is most likely to pick up.
Props
| Prop | Effect |
|---|---|
title | The whole title, as the browser tab and the search result show it |
description | One or two sentences, ideally under 160 characters |
canonical | The page's preferred address. Defaults to its address on site |
image, imageAlt | The preview image, as a path or an imported image, and its text alternative |
type | website, article, or profile. Defaults to article with a citation, else website |
siteName, locale | The site's name and its language, such as en_US, for previews |
twitter | The site's account on X, such as @yourname |
noindex | Keeps the page out of results, and drops the canonical link |
published, modified | Dates for an article, as ISO text or a Date |
person | name, url, jobTitle, affiliation, sameAs, image: a Person record |
citation | title, authors, date, journal or conference, volume, issue, firstPage, lastPage, pdf, doi, arxiv, abstract |
breadcrumbs | The trail from the home page to this one, as name and href pairs. Search results may show it in place of the address |
schema | Further schema.org records, written out as given |
sitemap | The sitemap's address, for a link in the head |
Check the result
After a deploy, paste a page's address into the Schema Markup Validator to see the records it reads, and into Google's Rich Results Test to see which ones Google can show, and into a link preview of any messaging app to see the card. These docs use the component on every page; Using with Astro has the rest of the layout.