astro-mystv0.3.0

Each file of a MyST collection becomes one entry. Entries work like those of Astro’s glob() loader:

FieldDescription
idMade from the file path like glob() does: methods.md → methods, chapter-1/index.md → chapter-1, the root index.md → index. A slug in the frontmatter of a Markdown file replaces it. See Routing and navigation.
dataYour frontmatter, the keys MyST knows and myst, described below
bodyThe source without its frontmatter: the Markdown of a .md file, the full text of a .tex file, nothing for notebooks. Useful for a reading time or a search index.
filePathThe source file, relative to the Astro project root, for example src/content/notes/methods.md
renderedThe page’s HTML (rendered.html) and metadata, used by render(entry)

entry.data#

data combines three kinds of keys.

Frontmatter as written#

Every key of a Markdown file’s frontmatter is in data as you wrote it, including keys that MyST does not know, such as pubDate, draft, heroImage or section (mystmd itself drops those). YAML is parsed as Astro parses Markdown frontmatter, so a date such as 2024-05-01 becomes a Date. Type your keys by extending the schema.

The keys in the next table are the exception: they hold MyST’s normalized values. Notebooks and LaTeX files only have those.

Keys normalized by MyST#

mystmd validates and normalizes the frontmatter. These keys hold MyST’s values, even where you wrote them differently (for example authors: Jane Doe becomes a list with one author):

FieldTypeDescription
titlestringThe frontmatter title, else the first heading, else LaTeX’s \title, else the entry id
subtitlestring?
short_titlestring?A shorter title, meant for navigation
descriptionstring?
dateDate?
tags, keywordsstring[]?
authorsAuthor[]{ name, orcid?, email?, corresponding?, affiliations? } plus MyST’s other author fields. affiliations lists names: MyST’s affiliation ids are resolved. Default: [].
thumbnailstring?Public URL of the page thumbnail
bannerstring?Public URL of the page banner
doistring?

Some keys of the MyST project options are inherited by every page that does not set its own, as in MyST: for example authors, date, license and numbering (see project).

myst#

Computed by the loader:

FieldTypeDescription
myst.urlstringURL of the page: Astro’s base, the loader’s route and the entry id, with trailingSlash applied
myst.ordernumberPosition in the collection’s table of contents, from 0
myst.levelnumberNesting level in the table of contents (1 = top level)
myst.enumeratorstring?Page number such as "2.1", when numbering.title is enabled
myst.kindstring"Article", or "Notebook" for Jupyter notebooks
myst.parents{ title, id? }[]The toc groups and parent pages that contain the page, outermost first: { title } for a group (a toc entry with a title but no file), { title, id } for a parent page. [] at the top level.
myst.partsRecord<string, string>?HTML of each part given in the frontmatter, such as abstract or acknowledgments, keyed by part name
myst.downloadsDownload[]?The files listed in the frontmatter downloads, as { title, url, filename?, format? }, with url where the site serves the file
myst.exportsExport[]?Files exported with the exports option: { title, url, filename, format, scope }, this page’s files (scope: 'page') and then the whole collection’s (scope: 'collection')

See Routing and navigation for sidebars, breadcrumbs and prev/next links built from these fields, and Exports for download links.

Frontmatter parts#

Parts written in the frontmatter, such as abstract: …, are part of the page’s HTML too, as in MyST’s themes: abstract, summary and keypoints come before the content, the other parts (acknowledgments, data_availability, …) after it, before the footnotes and the bibliography. Each one is a section.myst-part with a heading, like parts written in the body. myst.parts holds the same HTML, for example to show the abstract in a list of posts:

{post.data.myst.parts?.abstract && <Fragment set:html={post.data.myst.parts.abstract} />}

Downloads#

mystmd resolves the frontmatter downloads: files are copied with the page’s images, and exports referenced by their id become files. The raw value stays in data.downloads as you wrote it; use myst.downloads for links:

downloads:
  - file: data/results.csv
    title: Results
  - url: https://doi.org/10.5281/zenodo.1234
    title: Dataset on Zenodo
<ul>
  {post.data.myst.downloads?.map((file) => <li><a href={file.url} download={file.filename}>{file.title}</a></li>)}
</ul>

render(entry)#

import { render } from 'astro:content';

const { Content, headings, remarkPluginFrontmatter } = await render(entry);
  • Content renders the page body. The title is not part of it, so place it in your layout. Frontmatter parts are included; footnotes and the bibliography are appended to the body, unless a {bibliography} directive places the references.
  • headings lists the headings as { depth, slug, text }. slug is the heading’s element id, and text includes the section number when heading numbering is enabled, for example 1.2 Methods.
  • remarkPluginFrontmatter is the page’s complete frontmatter as normalized by MyST (numbering, math macros, license, exports, …), including the keys inherited from the project options. Keys that MyST does not know are not in it.

entry.rendered?.html holds the same HTML as a string, for example for a feed or a search index.

Typing your own frontmatter#

The loader’s default schema is mystSchema, exported from astro-myst/loaders. It types the MyST keys and myst, and lets any other key through untyped. To type your own keys, extend it in the collection’s schema:

// src/content.config.ts
import { defineCollection } from 'astro:content';
import { z } from 'astro/zod';
import { mystLoader, mystSchema } from 'astro-myst/loaders';

const blog = defineCollection({
  loader: mystLoader({ base: './src/content/blog' }),
  schema: ({ image }) =>
    mystSchema.extend({
      pubDate: z.coerce.date(),
      draft: z.boolean().default(false),
      heroImage: image().optional(),
    }),
});

export const collections = { blog };

Start from mystSchema: a collection’s schema replaces the loader’s, and a plain z.object() would drop myst and the MyST keys.

Then post.data.pubDate is a Date and post.data.draft a boolean, and you can filter on them:

import { getCollection } from 'astro:content';

const posts = await getCollection('blog', ({ data }) => !data.draft);

image() works for your own keys, whose values are paths relative to the file, as in Astro’s Markdown. It does not work for thumbnail and banner: MyST resolves those to URLs itself.

Types#

import type { CollectionEntry } from 'astro:content';

type Post = CollectionEntry<'blog'>; // Post['data'] follows the collection's schema

MystEntryData, exported from astro-myst and astro-myst/loaders, is the type of data with the default schema.