Skip to content
Polytope

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.

src/layouts/Page.astro
---
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 noindex rule, 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:

sh
npx astro add sitemap

It 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:

src/pages/robots.txt.ts
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.

astro
<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.

astro
---
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

PropEffect
titleThe whole title, as the browser tab and the search result show it
descriptionOne or two sentences, ideally under 160 characters
canonicalThe page's preferred address. Defaults to its address on site
image, imageAltThe preview image, as a path or an imported image, and its text alternative
typewebsite, article, or profile. Defaults to article with a citation, else website
siteName, localeThe site's name and its language, such as en_US, for previews
twitterThe site's account on X, such as @yourname
noindexKeeps the page out of results, and drops the canonical link
published, modifiedDates for an article, as ISO text or a Date
personname, url, jobTitle, affiliation, sameAs, image: a Person record
citationtitle, authors, date, journal or conference, volume, issue, firstPage, lastPage, pdf, doi, arxiv, abstract
breadcrumbsThe trail from the home page to this one, as name and href pairs. Search results may show it in place of the address
schemaFurther schema.org records, written out as given
sitemapThe 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.