Getting Started

Project Structure

A Meno project is a normal Astro project. There is no proprietary database and no opaque build artifact: your site is a folder of .astro files under src/pages/ and src/components/, Astro content collections under src/content/, and a handful of JSON config files at the root. You can open it in any editor, read it in a pull request, and build it with astro build like any other Astro site.

What makes it a *Meno* project is the convention those files follow — the meno-astro dialect, a constrained subset of Astro that the editor can parse back into its visual model. Because the files are the source of truth, you edit the project two ways that stay in sync: visually in the Meno editor, or by hand in the .astro files. The rest of this guide walks the layout.

The directory tree

A typical project looks like this:

my-site/
  src/
    pages/                      # pages — the route tree
      index.astro               #   -> /
      about.astro               #   -> /about
      blog/[slug].astro         # CMS template page (schema in meta.cms)
    components/                 # reusable components (subfolders -> categories)
      ui/Button.astro
      ui/Heading.astro
      section/Footer.astro
    content/                    # CMS items, one folder per collection
      blog/
        my-first-post.json      #   a published item (<id>.json)
        draft-idea.draft.json   #   an unpublished draft (<id>.draft.json)
    content.config.ts           # Astro content-collection config (generated)
    cmsComponents.ts            # registry for components in rich text (generated)
  project.config.json           # project config (locales, breakpoints, fonts, format)
  colors.json                   # theme colors (var(--text), var(--bg), …)
  variables.json                # CSS design tokens
  astro.config.mjs              # Astro config with the meno() integration
  images/  fonts/  icons/       # assets, referenced with absolute paths

Everything here is created and managed for you through the editor — but it is all plain text you are free to read, hand-edit, and commit.

src/pages — the route tree

Each .astro file under src/pages/ is a page, and Astro's file-based routing turns its path into a URL. A page is frontmatter (a const meta = {…} block plus component imports) followed by the node tree wrapped in <BaseLayout meta={meta}>. From src/pages/index.astro:

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

const meta = {
  title: "Meno",
  description: "Visual Astro website builder"
};
---
<BaseLayout meta={meta}>
  <main>
    <Heading size={1} text="Welcome" />
  </main>
</BaseLayout>

const meta carries the page title, description, and SEO fields. The body is the visible content. Every component tag is Capitalized and has a matching local import in the frontmatter — for a top-level page that is ../components/<Name>.astro.

Filename-to-route mapping

The file path under src/pages/ is the URL:

File

Route

src/pages/index.astro

/

src/pages/about.astro

/about

src/pages/pricing.astro

/pricing

src/pages/blog.astro

/blog

src/pages/blog/[slug].astro

/blog/my-first-post, …

index.astro maps to the root /; every other filename becomes its path directly. Use lowercase letters, numbers, and hyphens in filenames so routes stay clean.

CMS template pages

A file like src/pages/blog/[slug].astro is a CMS template page: a dynamic Astro route (with getStaticPaths) that renders one page per item in a collection. Its meta.source is "cms" and its meta.cms object holds the collection's schema — the field definitions, the slug field, and the URL pattern. That schema is the source of truth for the collection, and the route directory (blog/) is derived from meta.cms.urlPattern (/blog/{{slug}}).

const meta = {
  title: "{{cms.title}}",
  source: "cms",
  cms: {
    id: "blog",
    name: "Blog Posts",
    slugField: "slug",
    urlPattern: "/blog/{{slug}}",
    fields: {
      title: { type: "string", label: "Title", required: true },
      slug: { type: "string", label: "URL Slug", required: true },
      content: { type: "rich-text", label: "Content" }
    }
  }
};

The import { getCollection }, the getStaticPaths() function, and const { cms } = Astro.props; lines above the meta block are derived boilerplate, regenerated from meta.cms on save — edit the schema, not those lines. See CMS for the full collection workflow.

src/components — reusable components

Each component is one .astro file: frontmatter declaring its props, a body of markup, and optional inline <style> and <script>. Props are declared exactly once, as the argument to resolveProps(Astro, {…}) — there is no separate interface Props. From src/components/ui/Heading.astro:

---
import { resolveProps, style } from 'meno-astro';

const { text, size, class: className } = resolveProps(Astro, {
  text: { type: "string", default: "Heading" },
  size: { type: "number", default: 2 }
});

const __meno = { category: "ui" };
---
<h2 class={style({ base: { fontWeight: "500" } })}>{text}</h2>

The resolveProps argument is authoritative: it is what the editor reads back to populate the Properties panel, and what you edit to change a component's props. Keep class: className in the destructure so every instance can carry wrapper styles.

Subfolders become categories in the editor's component sidebar. This project groups its components into ui/, section/, form/, and cms/, and a component records its category in const __meno. See Building Components for the full anatomy.

src/content — CMS items

Collection items live as JSON files under src/content/<collection>/, using Astro's content layer. Each file is named for its stable _id (the filename stem) and holds the item's field values:

{
  "_id": "my-first-post",
  "_createdAt": "2026-01-15T10:30:00Z",
  "title": "My First Post",
  "slug": "my-first-post",
  "content": { "type": "doc", "content": [] }
}

A published item is <id>.json. An unpublished edit is a sibling <id>.draft.json; in development the draft is previewed over its published sibling, while a production astro build ships only the published files. The collection's *schema* is not stored here — it lives in the template page's meta.cms (see above). These folders hold only content.

Generated files (don't hand-edit)

Two files in src/ are regenerated by Meno and should not be edited by hand:

  • src/content.config.ts — Astro's content-collection config. It defines one collection per CMS template using meno-astro's menoCmsLoader (which loads <id>.json plus dev-only .draft.json sidecars) with a permissive schema, so astro build can resolve the collections. The real field schema stays in each template's meta.cms.

  • src/cmsComponents.ts — a registry (an eager import.meta.glob over src/components/) used by richTextWithComponents() to render components embedded inside CMS rich-text fields.

Config files

Four files at the project root configure the whole site:

File

What it holds

project.config.json

Locales (i18n), responsive breakpoints, fonts, siteUrl, icons, and format: "astro".

colors.json

Theme colors, exposed as CSS variables like var(--text) and var(--bg).

variables.json

CSS design tokens — typography and layout values reused across styles.

astro.config.mjs

The Astro config, registering the meno() integration.

project.config.json is where project-wide settings live. Its format: "astro" key is what marks the project as a meno-astro project, i18n declares the site's locales, and breakpoints defines the tablet and mobile widths used for responsive editing.

colors.json defines one or more themes, each a set of named colors. Every color becomes a CSS custom property, so you reference them as var(--text), var(--bg), var(--primary), and so on — never as raw hex values, so they adapt when the theme changes. See Project Configuration for the full set of keys.

astro.config.mjs is a normal Astro config with one addition — the meno() integration, which wires up locale routing, the style() utility CSS, and the runtime helpers the emitted markup imports:

import { defineConfig } from 'astro/config';
import meno from 'meno-astro/integration';

export default defineConfig({
  integrations: [meno()],
});

Assets

Images, fonts, and icons live in images/, fonts/, and icons/ at the project root and are referenced by absolute path:

<img src="/images/hero.webp" alt="" />

The editor's file picker browses these folders when you set a file-type prop, and the paths resolve unchanged in the build.

Two-way sync

The editor and the files are the same project from two angles. Edits in the Meno editor write .astro files; editing a .astro file changes what the editor shows. The meno-astro codec (its emit and parse functions) bridges the two and guarantees a lossless round-trip — as long as your hand-edits stay inside the dialect grammar. Write styles inside style({…}) rather than a raw class="…", i18n values inside i18n({…}), and so on; see Hand-Editing for the rules.

Here is how the editor's panels map to the filesystem:

Editor panel

Filesystem

Pages panel

src/pages/

Components panel

src/components/

CMS panel

src/content/ (items) and template pages' meta.cms (schema)

Settings

project.config.json, colors.json, variables.json


Because the whole project is plain text on disk, it is fully version-controllable with Git: branch it, diff it, review it in a pull request, and deploy it from CI. See GitHub for connecting a repository, and Installation to get started.

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

Product

Resources

Comparisons

© 2026 Meno. All rights reserved.