Guides

Creating Pages

A Meno page is a .astro file under src/pages/. There is no separate page database, no export step, no JSON blob — the file you see in the editor is the file on disk, and its path is its URL. Create src/pages/about.astro and you have a page at /about. Open it in the Meno editor and you edit the same file visually.

This guide covers where pages live, the anatomy of a page file, the meta block that drives SEO and routing, and how to add content with HTML elements, components, and links.

The filename is the route

Astro maps the src/pages/ directory to your site's routes. The file path is the URL path: drop the .astro extension, and index.astro collapses to its parent directory. Nested folders become nested routes.

File

URL

src/pages/index.astro

/

src/pages/about.astro

/about

src/pages/pricing.astro

/pricing

src/pages/blog/index.astro

/blog

src/pages/legal/privacy.astro

/legal/privacy

To create a page in a folder, just create the folder: src/pages/legal/privacy.astro makes /legal/privacy. The editor groups pages by folder automatically, and there is no limit to how deep you nest.

Anatomy of a page

Every Meno page has the same two-part shape: a frontmatter block (between the --- fences) holding imports and a const meta, then a body wrapped in <BaseLayout meta={meta}>. Here is a complete minimal page:

---
import { i18n } from 'meno-astro';
import { BaseLayout } from 'meno-astro/components';
import Heading from '../components/Heading.astro';

const meta = {
  title: "About",
  description: "About this site"
};
---
<BaseLayout meta={meta}>
  <main>
    <Heading size={1} text={i18n({ _i18n: true, en: "About", pl: "O nas" })} />
  </main>
</BaseLayout>

Three things make this a valid Meno page:

  • **const meta = {…}** — a plain const, holding the page's title, description, and other metadata. Write const meta, not export const meta, and don't add a satisfies type annotation — Meno reads this literal directly.

  • **<BaseLayout meta={meta}>** — the wrapper every page body lives inside. BaseLayout (imported from meno-astro/components) renders the <html>, <head> (title, description, Open Graph tags, hreflang), and the per-locale context, then slots your body in. Your content always goes between <BaseLayout meta={meta}> and </BaseLayout>.

  • Component imports — each capitalized component you use needs a matching local import in the frontmatter. Imports are alphabetized and use paths relative to the page file: a top-level page (src/pages/about.astro) imports from '../components/...'; a page one folder deeper imports from '../../components/...'.

Runtime helpers come from meno-astro (i18n, style, and others); the layout and chrome components come from meno-astro/components. Your own components come from ../components/.

You don't have to write this by hand — the editor generates it as you build. But knowing the shape is what lets you hand-edit a page and have it stay in sync. See Hand-editing.

The meta block

meta controls the page's <head> — SEO, social sharing, and routing. All fields are optional except, in practice, title and description.

Field

Purpose

title

Browser tab and search-result title

description

Search-result and social snippet

keywords

SEO keywords (minor weight)

ogTitle

Open Graph (social share) title; falls back to title

ogDescription

Open Graph description

ogImage

Open Graph image path, e.g. /images/og.webp

ogType

Open Graph type, e.g. "website"

slugs

Per-locale URL segments (see below)

A typical meta from a real page:

const meta = {
  title: "Meno - Astro Visual Builder",
  description: "Visual Astro website builder with native AI and team collaboration.",
  ogTitle: "Meno - AI Website Builder",
  ogImage: "/images/og.webp",
  ogType: "website"
};

Localized titles and slugs

If your project has multiple locales, the text fields can be i18n values instead of plain strings, using the { _i18n: true, en, pl, … } shape. BaseLayout resolves them per locale at render:

const meta = {
  title: { _i18n: true, en: "About Us", pl: "O nas" },
  description: {
    _i18n: true,
    en: "Learn more about our company",
    pl: "Dowiedz się więcej o naszej firmie"
  },
  slugs: { en: "about", pl: "o-nas", de: "uber" }
};

meta.slugs gives each locale its own URL segment, so the English page at /about becomes /pl/o-nas in Polish and /de/uber in German. The filename stays the default-locale URL; slugs only translates the path for other locales. For the full picture, see Internationalization.

Adding content

Inside <BaseLayout>, the body is your page's node tree. There are a few building blocks.

HTML elements. Any standard tag, with styles in style({…}) rather than a raw class string. The argument is a style object with base, and optional tablet / mobile breakpoints:

<div class={style({ base: { display: "flex", gap: "12px" } })}>
  <p class={style({ base: { color: "var(--text)" } })}>Hello</p>
</div>

This is the one rule that matters most: styles always live in style({…}), never in class="flex gap-3". A raw class string won't survive a save. See Styling for the full style object.

Component instances. Reusable blocks are capitalized tags. Each one needs a matching import in the frontmatter, and props are passed as JSX attributes — strings as text="Hi", numbers as size={1}, booleans as isMarginTop={true}, objects as link={{ href: "/x" }}, and i18n strings wrapped in i18n({…}):

<Section theme="primary">
  <Heading size={1} text="Build faster" align="center" />
  <Button text="Get started" link={{ href: "/download" }} />
</Section>

To learn how to make your own, see Building Components.

Links. Use the <Link> component (from meno-astro/components) for navigation; it localizes internal hrefs to the active locale automatically:

<Link href="/pricing">See pricing</Link>

You can also render lists, conditionals, and embeds in the body — see Node Types for the complete set.

Visual and hand-editing stay in sync

A Meno page has one source of truth — the .astro file — and two ways to edit it. Drag, type, and style in the Meno editor, and your changes are written straight back to the file. Open the same file in your code editor, change it, and the editor reflects it. The meno-astro codec parses the file into the editor's model on open and serializes it back on save, guaranteeing a lossless round-trip.

The one condition: hand-edits must stay inside the dialect — styles in style({…}), i18n in i18n({…}), templates as {expr}, component props as JSX attributes. Write a raw class="…" or ad-hoc logic and it won't round-trip. The dialect is small and the editor never produces anything outside it, so the simplest rule is to match what the generated files already look like. See Hand-editing for the grammar and the do/don't list.

CMS template pages

A page can also be a template that generates one route per item in a content collection — for example, one page per blog post. These live at src/pages/<collection>/[slug].astro as an Astro dynamic route, and their meta carries source: "cms" plus a meta.cms schema describing the collection's fields. The body renders the current item with {i18n(cms.field)} bindings.

const meta = {
  title: "{{cms.title}} | Blog",
  source: "cms",
  cms: { id: "blog", slugField: "slug", urlPattern: "/blog/{{slug}}", fields: { /* … */ } }
};

Defining collections, fields, and rendering CMS content is a topic of its own — see CMS.


A page is a file, the filename is the route, and the anatomy is always the same: const meta plus a <BaseLayout meta={meta}> body. From here, fill the body with components and styles — visually or by hand, on the same file.

Building the future of digital experiences, one website at a time

Product

Resources

Comparisons

© 2026 Meno. All rights reserved.