How do I use SEO file generation?

Enable option

// nutin.config.js
export default {
  generateSEOFiles: true,
}

route.title is also consumed at runtime: on every route change, document.title is set from the matching route's title here (falling back to the view's viewName when generateSEOFiles is off or no route matches) — see How do I create a view?.

Configuration file

// `config/seo.json` 
{
  "baseUrl": "https://my-nutin-app.eu",
  "disallowBots": [],
  "routes": [ /* ... */ ]
}

Default example

{
  "baseUrl": "https://my-nutin-app.eu",
  "disallowBots": ["GPTBot"],
  "routes": [
    {
      "path": "/",
      "title": "My Nutin App — Home",
      "description": "Welcome to My Nutin App.",
      "ogImage": "/assets/images/og-cover.jpg"
    },
    {
      "path": "/blog/:slug",
      "title": "My Nutin App — Blog",
      "description": "Read the latest posts.",
      "mockParams": { "slug": "hello-world" },
      "mockFetch": { "/api/posts/hello-world": { "title": "Hello World", "body": "..." } }
    }
  ]
}

With i18n option enabled

See How do I use i18n to help you enable i18n in your project.

// With i18n enabled and, in this example, `config/languages.json` listing `en`+`fr`
{
  "baseUrl": "https://my-nutin-app.com",
  "disallowBots": [],
  "routes": [
    {
      "path": "/",
      "title": { "en": "My Nutin App — Home", "fr": "Mon App Nutin — Accueil" },
      "description": { "en": "Welcome to My Nutin App.", "fr": "Bienvenue sur Mon App Nutin." },
      "ogImage": "/assets/images/og-cover.jpg"
    },
    // Second route left flat - SEO file will be generated but not localized
    {
      "path": "/blog/:slug",
      "title": "My Nutin App — Blog",
      "description": "Read the latest posts.",
      "mockParams": { "slug": "hello-world" }
    }
  ]
}

ogImage stays flat even on the localized route.

Route fields

Field Required Type Notes
path yes string Must match a key in src/app/routes.ts's appRoutes; dynamic segments use :param (:param? for optional).
title yes string | { [lang]: string } A flat string applies to every language uniformly; use a per-language object to localize.
description yes string | { [lang]: string } Same rules as title.
ogImage no string | { [lang]: string } Only written into og:image/twitter:image when present — omit to leave those tags out entirely.
mockParams required if path has dynamic segments { [param]: string } One representative value per dynamic segment, used to SSR-render that route at build time; missing one is a build-time exit(1).
mockFetch no { [url]: jsonBody } Exact-URL-keyed map — any fetch(url) call made while SSR-rendering this route that matches a key resolves to jsonBody instead of hitting the network. For views that fetch data on mount.
disallow no true | string[] true blocks this path for every bot in robots.txt; an array of bot names blocks it only for those bots.