Component Props
A component's props are declared once, in the argument to resolveProps(Astro, {…}) in the component's frontmatter. That object is the single source of truth: it lists every prop the component accepts, each prop's type, and its default value. There is no separate interface Props block and no __meno_props — the one resolveProps argument is what the editor reads, what the Meno codec round-trips, and what the component destructures to use in its markup.
This is the same definition the old Meno JSON interface carried, expressed as a JavaScript object literal instead.
The resolveProps argument
Each entry in the argument is name: { type, default, … }. resolveProps merges the incoming Astro.props over each prop's default at runtime, then returns the resolved values — which you destructure on the next line.
---
import { resolveProps, style } from 'meno-astro';
const { text, size, class: className } = resolveProps(Astro, {
text: { type: "string", default: "Heading" },
size: { type: "number", default: 1 }
});
const __meno = { category: "ui" };
---
<h2 class={style({ base: { fontWeight: "500" } })}>{text}</h2>Two rules always hold for the destructure:
Always keep
class: classNamein the destructure. Every component instance can carry wrapper styles from its parent, andclassNameis how those reach the component.Always emit the
resolvePropscall, even when the component has no props of its own — writeconst { class: className } = resolveProps(Astro, {});.
The TypeScript types of the destructured locals are inferred from the definition, so you never hand-write them. A number def types its local as number, a boolean as boolean, a link as { href; target? }, and a select as a union of its options (a string fallback otherwise). Change a prop by editing its entry in the resolveProps argument; the names and types regenerate from it on save.
When a component applies styles, it captures the resolved props in a const __props = resolveProps(Astro, {…}) first, then destructures from __props, because style() needs __props to resolve prop-bound style values:
---
import { resolveProps, style } from 'meno-astro';
const __props = resolveProps(Astro, {
text: { type: "string", default: "Resources" },
isOpen: { type: "boolean", default: false }
});
const { text, isOpen, class: className } = __props;
---
<li class={style({ base: { listStyle: "none" } }, __props, { root: true })}>{text}</li>See Building Components for the full anatomy of a component file.
Prop types
These are the valid prop types. Each entry is { type: "…", default: …, … }.
Type | Definition | Example |
|---|---|---|
| Single-line text |
|
| Numeric value |
|
| True/false toggle |
|
| Dropdown; needs |
|
| URL with optional target |
|
| File path; restrict with |
|
| HTML content |
|
**There is no image type.** For an image prop, use file with accept: "image/*".
string
A single line of text. Renders into markup as a {text} interpolation (or a {{template}} inside a string).
const { name, class: className } = resolveProps(Astro, {
name: { type: "string", default: "select" }
});number
A numeric value. The default and resolved local are real numbers, not strings.
const { size, class: className } = resolveProps(Astro, {
size: { type: "number", default: 1 }
});boolean
A true/false toggle. Useful for variants you switch with a conditional or a style _mapping.
const { isMarginTop, class: className } = resolveProps(Astro, {
isMarginTop: { type: "boolean", default: false }
});select
A dropdown limited to a fixed set of values. List the allowed values in options; the local's type is the union of those options. Most _mapping style bindings read a select prop.
const { variant, class: className } = resolveProps(Astro, {
variant: {
type: "select",
default: "primary",
options: ["primary", "secondary"]
}
});link
A URL with an optional target. The value is an object { href, target? }. Bind it to a <Link>'s href to make the destination editable per instance.
const { link, class: className } = resolveProps(Astro, {
link: { type: "link", default: { href: "#" } }
});file
A path to an uploaded asset. The accept property restricts the file type with a MIME pattern — accept: "image/*" for images, accept: "application/pdf" for PDFs.
const { image, alt, class: className } = resolveProps(Astro, {
image: { type: "file", accept: "image/*" },
alt: { type: "string", default: "" }
});rich-text
HTML content edited in the rich-text editor. Bind it into markup with <Fragment set:html={value} />, never a plain text interpolation — a rich-text value is structured, not a string.
const { question, answer, class: className } = resolveProps(Astro, {
question: { type: "rich-text", default: "Question?" },
answer: { type: "rich-text", default: "Answer goes here." }
});<dd><Fragment set:html={answer} /></dd>Rich-text props also back CMS rich-text fields — see CMS Fields for how richTextWithComponents renders embedded components.
Defaults
Give every prop a default: so the component renders standalone and previews sensibly in the editor. resolveProps layers the incoming Astro.props over the defaults, so any prop a parent doesn't pass falls back to its default. A string/number/boolean/select default is a literal of that type; a link default is { href, target? }; a file default is a path string (often ""); a rich-text default is an HTML string.
A prop may omit default (for example a file or boolean with no sensible fallback). The resolved local is then undefined until an instance supplies a value — guard it where you use it (src={image || undefined}).
Passing props to a component
When you place a component, its props are JSX attributes on the capitalized tag. The component needs a matching local import in the frontmatter (import Button from '../components/ui/Button.astro').
string →
text="Hi"(usetext={"a \"quoted\" value"}if it contains quotes or newlines)number →
size={1}boolean →
isMarginTop={true}object / link →
link={{ href: "/x", target: "_blank" }}internationalized →
text={i18n({ _i18n: true, en: "About", pl: "O nas" })}
<Heading size={1} text={i18n({ _i18n: true, en: "About Us", pl: "O nas" })} align="center" />
<Button isMarginTop={true} text="Services" link={{ href: "/services" }} />Any localized value goes through i18n({ _i18n: true, … }) so the right locale resolves at render time. See Template Expressions for how {{expr}} model values map to {expr} markup.
Reserved name: children
Never declare a prop named children. It's reserved for the child nodes passed into the component — the content that fills the component's <slot />. If you need a text prop, name it text, content, or anything else. (resolveProps skips children in the destructure for this reason.)
A component with a <slot /> receives nested content as children; a component without one takes only its declared props. See Node Types for slots and the other node forms.
Props are the contract between a component and the pages that use it. Keep the resolveProps argument tidy and well-defaulted, and both the visual editor and hand-edits stay in sync.