Shared content

Some passages belong on many pages: install steps, a note about a beta feature, a support link. Write such a passage once, in a file whose name starts with _, and show it wherever it belongs with <import-content>.

Share a passage

Put the passage in its own file:

content/
  _install.mdx
  guides/
    getting-started.mdx
    upgrade.mdx
Install PureStack with `yarn add purestack`, then start the site with `yarn purestack serve --content ./content`.

Then show it in each page, with a path relative to the page:

## Install

<import-content src="../_install.mdx"/>

The page shows the passage as if you had written it there. Markdown and components work, and its headings join the page's table of contents.

How shared files work

  • They are not pages. A file whose name, or the name of a folder above it, starts with _ is never published on its own, such as _install.mdx or _shared/beta-note.mdx.
  • They have no frontmatter. The page that shows a passage sets its title, template, and layout.
  • Links work from the page. A link inside a passage resolves from the page that shows it. When pages in different folders show the passage, start its links at the site root, such as /guides/styling/themes/.
  • They can import too. A shared file can show another shared file, or a file's code, with paths relative to the shared file itself.
  • Inside a component, markup rules apply. <import-content> works inside component markup too, such as a TabPane. There, the passage is read like anything else written inside a component: HTML, components, and code blocks work, but other Markdown, such as **bold**, lists, and [links](./page), stays as written. Write passages meant for components in HTML.
  • Edits show up. While purestack serve runs, saving a shared file updates every page that shows it.

Headers and footers use directory inheritance rather than <import-content>. Add content/header.mdx to supply the site's header:

<TopBar tone="neutral" variant="surface" />

Add content/footer.mdx for its footer:

<SiteFooter copyright="Acme documentation" />

A page uses the nearest header and the nearest footer found by walking up its source folders. For example, content/guides/header.mdx replaces the root header for pages under guides, while those pages can still inherit the root footer. The two lookups are independent. .rmdx is also supported; keep only one header and one footer per directory across these extensions.

Links in these sections resolve from the header or footer file, unlike links in an imported passage, which resolve from the consuming page. Use a content-root script path when a shared section loads browser code, or set PageScript.sourceRelPath explicitly. Frontmatter layout.showFooter: false hides the footer on one page. Custom templates receive headerHtml and footerHtml and decide where to place them.

Choose a passage, component, or template

Reuse requirement Use
The same prose on several pages An underscore-prefixed file and import-content
A shared header or footer for a section header.mdx / footer.mdx in its directory
Markup with inputs or slots A Regor component registered by a plugin
A different document shell A page template
Source code shown verbatim import-codeblock

When something is wrong

The build stops and names the page and the tag:

Content import "./_instal.mdx" in "guides/upgrade.mdx" does not match any file.

Most fixes are one of these:

  • A typo or different capitals. ./_Install.mdx doesn't find _install.mdx.
  • The file is not shared. Rename it so its name, or a folder above it, starts with _. A page cannot be imported.
  • The file has frontmatter. Remove it; the page's frontmatter applies.
  • Two files import each other. The message shows the chain, such as guides/upgrade.mdx → _install.mdx → _requirements.mdx → _install.mdx.