meno-astro Runtime API
Every Meno project is a real Astro project, and the .astro files Meno emits import a small runtime from the meno-astro package. These helpers turn the editor's data — style objects, i18n values, prop definitions, collection lists — into HTML at build time. The meno() Astro integration (registered in astro.config.mjs) wires the runtime up: it generates the build-time CSS, maps your locales into Astro's i18n routing, and injects the locale middleware.
You rarely call these by hand — the editor emits them whenever you save, and they round-trip back into the visual model on the next parse. But when you [hand-edit .astro files](/docs/hand-editing) it helps to know what each symbol does and where it shows up. This page is the reference for that runtime surface.
A typical page header shows the shape of it:
---
import { getCollectionList, i18n, queryList, style } from 'meno-astro';
import { BaseLayout, Embed, Link } from 'meno-astro/components';
import Heading from '../components/ui/Heading.astro';
---
<BaseLayout meta={meta}>
<Heading text={i18n(cms.title)} />
</BaseLayout>Helpers come from the package root (meno-astro); the runtime components come from meno-astro/components. Your own components are normal local imports.
meno-astro helpers
These are imported from meno-astro. Each appears in emitted markup wherever its concept is used — style() on every styled element, i18n() on every localized or CMS-bound value, the list helpers wherever you loop, and so on.
Helper | Signature | What it does |
|---|---|---|
|
| Resolves a Meno |
|
| Resolves an |
|
| The single authoritative prop block for a component. Merges |
|
| Tolerant prop-list slicing. Returns |
|
| Resolves a CMS collection list at build time into an array of items, ready to |
|
| In-memory filter / sort / limit over an already-fetched list. |
|
| Resolves a structured or prop-mapped href to a URL string. |
|
| Resolves a |
|
| Resolves a structured embed payload to an HTML string. |
|
| Renders a CMS rich-text field (a TipTap document) to HTML, including any components embedded in the text. |
|
| Renders a component root's prop-bound styles as an inline |
style
style() is how every styled element gets its class. The argument is a Meno StyleObject — a flat object, or a responsive { base, tablet, mobile } — and style() returns a class name. The actual CSS (utility rules, @media breakpoints, :hover and other interactive states) is generated at build time by the meno() integration, so the function only ever returns a string at render.
<div class={style({ base: { display: "flex", gap: "12px" }, mobile: { flexDirection: "column" } })}>The optional second argument is the host component's resolved props — pass __props so prop-bound { _mapping: … } values resolve to a concrete value before CSS is generated. The third argument carries emit-time metadata: interactive rules (which become :hover / state CSS), a label, and root: true on a component's structure root so the instance class merges in. See Style Properties and Interactive Styles for the style object shape.
<button class={style({ base: { padding: "12px 22px" } }, __props, { root: true, label: "button" })}>i18n
i18n() resolves a value for the page's active locale. Hand-written i18n values use the { _i18n: true, en, pl, … } shape; i18n() returns the right string for the current locale. Crucially, it is the identity function for non-i18n values — so it is safe to wrap any CMS-data binding, whether or not that field is localized.
<Heading text={i18n({ _i18n: true, en: "About", pl: "O nas" })} />
<span>{i18n(category.name)}</span>The locale context is opened once per render by the injected locale middleware, so every i18n() call on the page (and in its components) sees the same locale. See Internationalization.
resolveProps
resolveProps(Astro, defs) is the one authoritative prop block in a component — there is no separate interface Props. The defs argument is exactly what the dialect parser reads to know a component's props; at runtime it merges the incoming Astro.props over each definition's default and always returns a class (defaulting to ""). The destructured locals' TypeScript types are inferred from defs.
---
import { resolveProps, style } from 'meno-astro';
const __props = resolveProps(Astro, {
text: { type: "string", default: "Link" },
variant: { type: "select", default: "primary", options: ["primary", "secondary"] }
});
const { text, variant, class: className } = __props;
---Keep class: className in the destructure, and emit the call even for an empty prop set: const { class: className } = resolveProps(Astro, {});. See Component Props.
list
list() is the tolerant slicer behind a prop-bound list. It accepts a possibly-missing array and returns a safe array to map over, applying offset then limit.
{list(items, { limit: 6 }).map((item, itemIndex) => (
<li>{item.title}</li>
))}See Lists for the full looping grammar.
getCollectionList and queryList
getCollectionList() resolves a CMS collection into an array at build time. It is called in the frontmatter; the editor passes astro:content's getCollection as the last argument (the package never imports it itself). The result carries a synthesized _url and _id on each item.
const docsList = await getCollectionList("docs", {}, Astro, getCollection);queryList() runs an in-memory filter / sort / limit over a list you have already fetched — useful for a nested loop that narrows an outer collection by a loop variable:
{queryList(docsList, { filter: { field: "category", operator: "eq", value: category._id } }).map((doc) => (
<Link href={i18n(doc._url)}>{i18n(doc.title)}</Link>
))}See Filtering and Search for query shapes.
href and when
href() resolves a structured href (a { href, target? } object or a prop mapping) to a URL string, against the host component's props. when() resolves a BooleanMapping to a boolean — this is what powers a conditional bound to a prop.
{when({ _mapping: true, prop: "icon", values: { arrow: true, none: false } }, __props) && (
<Embed html={`<svg>…</svg>`} />
)}embedHtml
embedHtml() resolves a structured embed payload to an HTML string against the host props. It backs an embed node whose HTML varies by prop. (A static embed renders its HTML directly through the <Embed> component instead.)
richTextWithComponents
richTextWithComponents(value, cmsComponents) renders a CMS rich-text field — stored as a TipTap document — to HTML, including any project components embedded in that rich text. It is the emit target for a rich-text field bound as a text child, and is always used through set:html (it returns a promise, which set:html awaits natively):
<Fragment set:html={richTextWithComponents(cms.content, cmsComponents)} />The cmsComponents argument is the generated registry src/cmsComponents.ts (see below). Never interpolate a rich-text field as plain text — {i18n(cms.content)} would print [object Object] because the value is a structured document, not a string. See CMS.
inlineStyle
inlineStyle(decls, props?) renders a component root's prop-bound style declarations as an inline style="…" attribute — used for style values that depend on a prop at render time and so cannot become a build-time class. It drops any declaration the instance's own class already overrides (so the instance utility class wins), and returns undefined when nothing remains, letting Astro omit the attribute.
<img style={inlineStyle({ "height": `${height}`, "object-fit": `${objectFit}` }, __props)} />meno-astro/components
These are the runtime Astro components emitted markup imports from meno-astro/components. They are real components — Capitalized tags with a matching import — that you compose like any node type.
Component | Role |
|---|---|
| The page shell. Renders |
| An |
| A raw HTML / SVG injector that also normalizes rich-text values to HTML. |
| A locale switcher whose links point at the current page in each locale, slug-translated. |
BaseLayout
BaseLayout wraps every page. It takes the frontmatter meta object and renders the document shell: the <html lang> for the active locale, the <head> (with meta.title and meta.description resolved through i18n(), since they may be i18n values), the canonical link and hreflang alternates, and <body> with your page tree slotted in. It also pulls in the build-time stylesheet so the classes style() returned have CSS.
<BaseLayout meta={meta}>
<main>…</main>
</BaseLayout>See Creating Pages for the page skeleton.
Link
Link renders an <a> and localizes internal hrefs to the active locale (so /about becomes /pl/o-nas on a Polish page). The href can be a plain string, a link object, or an i18n / mapped href.
<Link href="/about">About</Link>
<Link href={i18n(doc._url)}>{i18n(doc.title)}</Link>Embed
Embed injects raw HTML or SVG via set:html. When bound to a CMS rich-text field, it normalizes the stored value (a TipTap document or { __richtext__, … } object) to HTML before injecting, and localizes any internal links inside it.
<Embed html={`<svg viewBox="0 0 24 24">…</svg>`} />
<Embed html={i18n(cms.body)} />LocaleList
LocaleList renders one link per configured locale, each pointing at the current page in that locale with the slug translated through the project's slug map; the active locale is marked. Its display and style sub-props (displayType, showFlag, itemStyle, activeItemStyle, …) are passed as a single meta={{…}} object, with each style wrapped in style(…). See Internationalization.
Supporting files
A few generated files complete the runtime. You normally do not hand-edit these — they are scaffolded by the converter and kept in sync.
src/content.config.ts— the Astro content-collection config. OnedefineCollectionper CMS collection, each usingmenoCmsLoader(readssrc/content/<collection>/*.json, merging.draft.jsonsidecars in dev) andresolveCmsEntrySlug(resolves an i18n slug to its default-locale string for routing). The collection's real schema lives in its template page'smeta.cms, not here — this file uses a permissive schema soastro buildcan resolve the collection.src/cmsComponents.ts— the generated component registry passed torichTextWithComponents(). It is a constantimport.meta.glob('./components/**/*.astro')keyed by component name, so components you create later join it automatically. Do not hand-edit it.The
meno()integration inastro.config.mjs— the default export ofmeno-astro/integration, added asintegrations: [meno()]. At build time it generates the utility/interactive CSS, maps yourproject.config.jsoni18n into Astro's native i18n routing, and injects the locale middleware that opens the per-render locale context.
import { defineConfig } from 'astro/config';
import meno from 'meno-astro/integration';
export default defineConfig({
integrations: [meno()],
});One more constant worth knowing: meno-astro exports dialectVersion, the on-disk format version written into generated projects. It lets a project be migrated forward if the dialect format ever evolves — you will not reference it by hand, but you may see it in tooling.