SEO¶
A page rendered by the server is read by a search engine as people read it : the text is in the HTML. The server writes its head from its settings.
export const config: PageConfig = {
title: 'Pricing', // <title>Pricing · My product</title> with titleTemplate '%s · My product'
description: 'Free to start, simple when you grow.', // <meta name="description"> and og:description
image: '/og.png', // og:image (a file of public/, or an address) and a large Twitter card
canonical: '/pricing', // <link rel="canonical"> : the whole address
index: false, // <meta name="robots" content="noindex">
};
Each may be made from the data of the page :
export const config: PageConfig = {
title: (data) => data.post.title,
description: (data) => data.post.summary,
canonical: (data) => `/posts/${data.post.slug}`,
};
| Head | From |
|---|---|
<title> |
title of the page (else of its layouts, else title of the website), in titleTemplate of site.config.ts |
description, og:description |
description |
og:title |
title |
og:image, twitter:card |
image, written as a whole address |
canonical |
canonical, written as a whole address : the origin, the base path, the path |
robots |
index: false of the page or of a layout, or seo.noIndex of the website |
More in the head¶
head(props) of a page or a layout adds meta tags and links, or any JSX :
export function head({ data }: PageProps<typeof loader>): Head {
return {
meta: [
{ property: 'og:type', content: 'article' },
{ property: 'article:published_time', content: data.post.date },
],
links: [{ rel: 'alternate', hreflang: 'fr', href: `/fr/posts/${data.post.slug}` }],
};
}
Structured data¶
JSON-LD is a script of the page : serialize() writes JSON nothing can break out of.
<script type="application/ld+json">{serialize({
'@context': 'https://schema.org',
'@type': 'Product',
name: data.product.name,
offers: { '@type': 'Offer', price: data.product.price, priceCurrency: 'USD' },
})}</script>
sitemap.xml and robots.txt¶
/sitemap.xmllists the pages of the website whose paths take no value, but the ones which saysitemap: falseorindex: false, and the pages for signed-in visitors.sitemap: { changefreq, priority }tells more./robots.txtallows every robot (seo.robots: 'allow'), none ('disallow'), or says your text ; it names the sitemap.- A website with pages whose paths take values (posts, products) writes its own : a page or an endpoint at
pages/sitemap.xml.tsanswers in place of the one of the server.
pages/sitemap.xml.ts
import { escape } from 'am-ssr';
import type { PageContext } from 'am-ssr';
export async function GET(ctx: PageContext) {
const home = ctx.site.origin + ctx.site.base;
const posts = await ctx.db.query({ instance: 'mongodb', database: 'blog', table: 'posts', select: 'slug updatedAt', limit: 5000 });
const urls = posts.map((post) => `<url><loc>${escape(home + '/posts/' + encodeURIComponent(post.slug))}</loc></url>`);
ctx.setHeader('content-type', 'application/xml; charset=utf-8');
return ctx.text(`<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">${urls.join('')}</urlset>`);
}
Addresses¶
seo.noIndex: truekeeps the whole website out of search engines (a staging copy).trailingSlash:/about/goes to/about('never', the default), the other way ('always'), or both answer ('ignore').redirectsofsite.config.tssend old addresses to new ones (308unless astatussays another ;/old/*takes every path below/old).- Behind a domain of its own, the addresses of the pages are at the root of the domain : see deployment.