New: SEO Blog Writer, a readable, checked blog post for your site every week. Pay once, download straight away.

See it
AI Skill Up
Browse skills

SEO for a Qwik City site: head tags, prerender and sitemaps

How to set titles, canonicals, Open Graph and JSON-LD in Qwik City, prerender every route with the static adapter, and add a sitemap. From a live Qwik site.

This site is built with Qwik City and prerendered to static HTML. Every page ships its head tags, structured data and a canonical URL, and a sweep checks them at build time. This is how that works, in the order you would build it.

Head tags per route

import type { DocumentHead } from "@builder.io/qwik-city";

export const head: DocumentHead = ({ resolveValue }) => {
  const post = resolveValue(usePost);
  const canonical = `https://example.com/blog/${post.slug}/`;
  return {
    title: post.seoTitle,
    meta: [
      { name: "description", content: post.summary },
      { property: "og:title", content: post.seoTitle },
      { property: "og:url", content: canonical },
    ],
    links: [{ rel: "canonical", href: canonical }],
  };
};

The root layout renders these values. Qwik City lets a layout's head and a route's head combine, so shared tags live once in the layout and each route adds its own.

Structured data

JSON-LD is a script tag with a JSON body. Return it from the head function as data and render it in the layout, or render it inside the route's component. Either way, build it from the same object the page renders from, so the two cannot disagree. Product schema for digital products has the shape for a product page.

Prerender every route

The static adapter renders every route to an HTML file at build time. Dynamic routes, like /blog/[slug], list their parameters with onStaticGenerate:

export const onStaticGenerate: StaticGenerateHandler = () => ({
  params: ARTICLES.map((a) => ({ slug: a.slug })),
});

Nothing renders at request time, so there is nothing for a crawler to wait for, and the site can be served from any static host.

Trailing slashes

Qwik City's static output serves pages at URLs ending in a slash. Make every canonical, internal link and sitemap entry use the same form, or you create redirects and duplicate signals. We lost time to exactly this, written up in the trailing slash canonical mismatch.

Sitemap and the rest

A small script after the build writes sitemap.xml from the same data the routes use, and the same script writes llms.txt. Then a sweep reads every built HTML file and fails the build if a page has no title, no canonical, a duplicate description, or a description outside the target length.

The look of this Qwik site is Warm Storefront UI, including the blog layout with topic filters, pagination and the sticky product sidebar. It is plain CSS custom properties, so it works in Qwik or any other stack.

Questions

Is Qwik good for SEO?

Yes. Qwik City renders full HTML on the server or at build time, so crawlers get content without running JavaScript, and its resumability keeps the amount of JavaScript on first load small.

How do I set the title and meta tags in Qwik City?

Export a head constant of type DocumentHead from the route. It can be an object or a function that reads route loader data, and the layout's head renders it into the page.

Does Qwik generate a sitemap?

The static adapter can write a sitemap during static generation. Many sites write their own small script instead, so they control which pages appear and can add extra files like llms.txt.

Or skip the afternoonand drop one in.