Pages

Pages are generated by JavaScript files in pages/. Each file must export a default function that returns an array of page objects.

export default function({ data, parse }) {
  const { html } = parse.markdown(`# ${data.site.name}`);

  return [
    {
      permalink: "/",
      template: "base",
      title: "Home",
      content: html,
    },
  ];
}

Directory Rules

pages/ is flat. This is valid:

pages/site.js
pages/posts.js

This is not valid:

pages/blog/posts.js

Use multiple files when it helps organize generators, but keep them directly inside pages/.

Page Object Contract

Every page object must have permalink.

{
  permalink: "/docs/intro/",
  template: "base",
  title: "Intro",
  content: "<h1>Intro</h1>"
}

Fields:

  • permalink is required and must be a string.
  • template is optional and must be relative to templates/.
  • content is optional and defaults to an empty string.
  • Any extra fields are preserved and exposed to templates through page.

layout is not supported. Use template.

Permalinks And Output Paths

Veta normalizes permalinks into output paths:

/                  -> index.html
/about/            -> about/index.html
/feed.xml          -> feed.xml
/llms.txt          -> llms.txt

If the last permalink segment has an extension, Veta writes that exact file path. Otherwise, it writes an index.html file under the permalink path.

Templated Pages

When template is present, Veta passes content unchanged and trusted to the named template. It does not automatically render Markdown or resolve components. The generator must return the final format that the template expects, usually HTML.

export default function({ parse }) {
  const source = "This supports **Markdown** and <note>components</note>.";
  const { html } = parse.markdown(source);
  const content = parse.renderComponents(html);

  return [
    {
      permalink: "/about/",
      template: "base",
      title: "About",
      content,
    },
  ];
}

The template receives that final string as page.content. Use parse.markdown(text) and parse.renderComponents(text) explicitly, in the order required by the content.

Raw Pages

When template is omitted, Veta writes content directly.

{
  permalink: "/feed.xml",
  content: `<feed><title>${data.site.name}</title></feed>`,
}

Raw pages are useful for feeds, sitemaps, JSON, text files, Markdown files, and any other generated asset.

Because raw output is not transformed either, a generator can intentionally return Markdown, JSON, XML, or plain text without a template.

Output Collisions

Two page objects cannot write the same output path. Veta reports an error if generators produce conflicting permalinks.

Generating Pages From Files

Use the JavaScript file API to generate content-driven pages:

export default function({ files, parse }) {
  return files.listFiles("content/posts/**/*.md").map((path) => {
    const { frontmatter, html } = parse.markdown(files.readFile(path));
    const content = parse.renderComponents(html);

    return {
      permalink: files.toPermalink(path, { stripPrefix: "content" }),
      template: "post",
      title: frontmatter.title,
      content,
    };
  });
}