app.json-Referenz
app.json beschreibt eine gehostete App vollständig: welche Bausteine sie zeigt, woher die Daten kommen, was bei einem Klick passiert. Der Builder schreibt diese Datei, im Bereich Code kannst du sie direkt bearbeiten. Beides ist gleichwertig – der Builder kann alles, was die Datei kann.
Diese Referenz entsteht aus der Runtime selbst (@methacore/runtime, Schema-Version 1). Das JSON-Schema liegt unter https://methacore.de/schemas/apps/app/v1.json; mit "$schema" bekommst du in jedem Editor Autovervollständigung.
Aufbau
{
"$schema": "https://methacore.de/schemas/apps/app/v1.json",
"schemaVersion": 1,
"name": "Pinnwand",
"auth": { "clientId": "cl_7d2e41a9", "scopes": ["mc.apps.storage:read", "mc.apps.storage:write"] },
"initialState": { "entwurf": "" },
"root": { "id": "root", "type": "Stack", "children": [] }
}
| Feld | Typ | Pflicht | Bedeutung |
|---|---|---|---|
$schema | string | nein | Adresse des JSON-Schemas, nur für Editoren |
schemaVersion | 1 | ja | Version des Formats. Andere Werte lehnt die Runtime ab. |
name | string | nein | Name der App. Das Studio hält ihn gleich mit dem DevHub-Eintrag. |
auth.clientId | string | für Daten | OAuth-Client der App. Ohne ihn kann die App keine Daten laden. |
auth.scopes | string[] | für Daten | Scopes, denen Mitglieder beim ersten Öffnen zustimmen. Siehe Berechtigungen. |
initialState | object | nein | Startwerte des State |
root | Knoten | ja | Der oberste Baustein, meist ein Stack |
Knoten
Jeder Baustein ist ein Knoten:
{
"id": "speichern",
"type": "Button",
"props": { "label": "Speichern", "variant": "filled" },
"bind": [],
"on": { "click": [{ "type": "toast", "message": "Gespeichert" }] },
"children": []
}
| Feld | Bedeutung |
|---|---|
id | eindeutig in der ganzen App. Taucht im HTML als data-mc-id auf – damit sprichst du den Baustein in styling.css an. |
type | einer der Bausteine unten |
props | Eigenschaften. Jeder Wert darf {{…}}-Ausdrücke enthalten. |
bind | Datenbindungen: Datenquellen, die beim Start geladen werden |
on | Ereignisse mit einer Liste von Aktionen |
children | Kind-Knoten – nur bei Containern: Stack, Grid, Card, Tabs, List |
Zusätzliche Felder am Knoten sind nicht erlaubt; die Runtime meldet einen Fehler, statt sie still zu ignorieren.
Bausteine
| Baustein | Im Builder | Gruppe | Kinder | Ereignisse |
|---|---|---|---|---|
Stack | Stapel | Layout | ja | load |
Grid | Raster | Layout | ja | – |
Card | Karte | Layout | ja | – |
Tabs | Tabs | Layout | ja | change |
Heading | Überschrift | Inhalt | – | – |
Text | Text | Inhalt | – | – |
Image | Bild | Inhalt | – | – |
Button | Button | Formular | – | click |
TextInput | Textfeld | Formular | – | change, submit |
Select | Auswahl | Formular | – | change |
Checkbox | Checkbox | Formular | – | change |
DateInput | Datum | Formular | – | change |
Table | Tabelle | Daten | – | rowClick |
List | Liste | Daten | ja | click |
Chart | Diagramm | Daten | – | – |
Zwei Eigenschaften hat jeder Baustein:
| Eigenschaft | Bedeutung |
|---|---|
visible | Ausdruck, der den Baustein ein- oder ausblendet. Ausgeblendet ist er bei false, "false", einem leeren Text oder wenn der Ausdruck auf nichts zeigt. Fehlt die Eigenschaft, ist er immer sichtbar. |
className | eigene CSS-Klassen, durch Leerzeichen getrennt. Im Builder heißt das Feld „CSS-Klasse“. |
Stack – Stapel
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
gap | Abstand (px) | Abstand zwischen den Elementen in Pixeln. Neu im Builder: 12. |
direction | Richtung | Ob die Elemente unter- oder nebeneinander stehen. Werte: column (Untereinander), row (Nebeneinander). |
label | Tab-Titel (in Tabs) | Beschriftung, die neben oder über dem Element steht. |
align | nur im Code | Ausrichtung quer zur Richtung: start, center, end oder stretch. |
justify | nur im Code | Verteilung entlang der Richtung: start, center, end oder between. |
Das load-Ereignis wirkt nur am Wurzelknoten. label braucht ein Stapel nur als Kind von Tabs.
Grid – Raster
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
columns | Spalten | Wie viele Spalten nebeneinander stehen. Neu im Builder: 2. |
minWidth | Mindestbreite je Spalte (px) | Schmaler wird keine Spalte; darunter bricht das Raster um. |
gap | Abstand (px) | Abstand zwischen den Elementen in Pixeln. Neu im Builder: 12. |
label | Tab-Titel (in Tabs) | Beschriftung, die neben oder über dem Element steht. |
Mit minWidth bricht das Raster von selbst um; sonst gilt columns (1–12).
Card – Karte
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
title | Titel | Überschrift der Karte. Neu im Builder: "Karte". |
label | Tab-Titel (in Tabs) | Beschriftung, die neben oder über dem Element steht. |
label braucht eine Karte nur als Kind von Tabs.
Tabs – Tabs
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
defaultTab | nur im Code | Index des Tabs, der zuerst offen ist, ab 0. |
Jedes Kind ist ein Tab; sein props.label ist die Beschriftung und Pflicht. change liefert { value: <Index> }.
Heading – Überschrift
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
text | Text | Angezeigter Text. {{…}} setzt Werte aus dem State ein. Neu im Builder: "Überschrift". |
level | Ebene | Größe der Überschrift, H1 ist die größte. Werte: 1 (H1), 2 (H2), 3 (H3), 4 (H4). Neu im Builder: 2. |
Text – Text
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
text | Text | Mit {{state.schlüssel}} fügst du Werte ein. Neu im Builder: "Text". |
size | Größe | Schriftgröße des Texts. Werte: sm (Klein), md (Normal), lg (Groß). |
muted | Gedämpft | Grauer, zurückhaltender Text für Nebeninformationen. |
align | nur im Code | Ausrichtung: left, center oder right. |
Image – Bild
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
src | Bild-URL (https) | Adresse des Bilds, muss mit https beginnen. |
alt | Alternativtext | Beschreibung für Screenreader und wenn das Bild nicht lädt. |
width | Breite (px) | Breite in Pixeln, leer für die volle Breite. |
radius | Ecken (px) | Abrundung der Ecken in Pixeln. |
Geladen werden nur Adressen mit https: oder data:image/.
Button – Button
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
label | Beschriftung | Beschriftung, die neben oder über dem Element steht. Neu im Builder: "Button". |
variant | Variante | Aussehen des Knopfs. Werte: filled (Gefüllt), light (Hell), outline (Umrandet), subtle (Dezent). |
fullWidth | Volle Breite | Nimmt die ganze verfügbare Breite ein. |
disabled | Deaktiviert | Sperrt das Element, es lässt sich nicht bedienen. |
color | nur im Code | red macht einen roten Knopf, z. B. zum Löschen (Klasse .mc-button--danger). |
Die Klasse ist .mc-button--primary für gefüllte, .mc-button--secondary für helle, umrandete und dezente Knöpfe.
TextInput – Textfeld
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
label | Beschriftung | Beschriftung, die neben oder über dem Element steht. Neu im Builder: "Textfeld". |
model | State-Schlüssel | Der eingegebene Wert landet in diesem State-Schlüssel. |
placeholder | Platzhalter | Grauer Hinweis im leeren Feld, z. B. ein Beispiel. |
type | Typ | Art der Eingabe, steuert Tastatur und Prüfung. Werte: text (Text), email (E-Mail), number (Zahl), search (Suche). |
required | Pflichtfeld | Das Feld muss ausgefüllt werden. |
value | nur im Code | Startwert, wenn kein model gesetzt ist. |
disabled | nur im Code | Sperrt das Feld. |
change kommt bei jeder Eingabe mit { value }, submit bei Enter. Mit type: "number" landet eine Zahl (oder null) im State.
Select – Auswahl
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
label | Beschriftung | Beschriftung, die neben oder über dem Element steht. Neu im Builder: "Auswahl". |
model | State-Schlüssel | Der eingegebene Wert landet in diesem State-Schlüssel. |
options | Optionen | Liste wie ["A", "B"] oder [{"value": "a", "label": "A"}], oder {{state.liste}}. Neu im Builder: ["Option 1","Option 2"]. |
optionValue | Feld für den Wert | Feld jeder Zeile, das bei der Auswahl gespeichert wird. |
optionLabel | Feld für den Text | Feld jeder Zeile, das in der Auswahl zu lesen ist. |
placeholder | Platzhalter | Grauer Hinweis im leeren Feld, z. B. ein Beispiel. |
value | nur im Code | Startwert, wenn kein model gesetzt ist. |
disabled | nur im Code | Sperrt die Auswahl. |
options ist eine Liste von Texten oder Objekten. Mit optionValue und optionLabel passt eine Datenquelle direkt: "options": "{{state.gruppen}}", "optionValue": "id", "optionLabel": "name".
Checkbox – Checkbox
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
label | Beschriftung | Beschriftung, die neben oder über dem Element steht. Neu im Builder: "Checkbox". |
model | State-Schlüssel | Der eingegebene Wert landet in diesem State-Schlüssel. |
value | nur im Code | Startwert, wenn kein model gesetzt ist. |
disabled | nur im Code | Sperrt die Checkbox. |
Im State landet true oder false; change liefert { value }.
DateInput – Datum
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
label | Beschriftung | Beschriftung, die neben oder über dem Element steht. Neu im Builder: "Datum". |
model | State-Schlüssel | Der eingegebene Wert landet in diesem State-Schlüssel. |
withTime | Mit Uhrzeit | Zusätzlich zur Uhrzeit fragen. |
placeholder | nur im Code | Grauer Hinweis im leeren Feld. |
required | nur im Code | Das Feld muss ausgefüllt werden. |
disabled | nur im Code | Sperrt das Feld. |
value | nur im Code | Startwert, wenn kein model gesetzt ist. |
Im State landet der Wert des Browsers: 2026-10-02 bzw. mit Uhrzeit 2026-10-02T17:00.
Table – Tabelle
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
data | Datenquelle (Ausdruck) | Liste aus dem State, die die Tabelle zeigt. |
columns | Spalten (JSON) | z. B. [{"key": "name", "label": "Name"}]. Leer = alle Felder. |
empty | Text ohne Einträge | Steht da, solange es keine Einträge gibt. Neu im Builder: "Keine Einträge". |
columns darf auch eine Liste von Feldnamen sein: ["title", "start"]. Bei rowClick steht die Zeile als item (und event) in den Aktionen.
List – Liste
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
data | Datenquelle (Ausdruck) | Liste aus dem State, deren Einträge die Liste zeigt. |
itemText | Text je Eintrag (Ausdruck) | Gilt nur ohne Kind-Elemente. {{item.feld}} greift auf den Eintrag zu. |
empty | Text ohne Einträge | Steht da, solange es keine Einträge gibt. |
Ohne Kinder zeigt jeder Eintrag itemText. Mit Kindern werden sie je Eintrag gezeichnet; darin gibt es {{item}} und {{index}}. itemText wird je Eintrag ausgewertet. click liefert den Eintrag als item.
Chart – Diagramm
| Eigenschaft | Im Builder | Bedeutung |
|---|---|---|
data | Datenquelle (Ausdruck) | Liste aus dem State, die das Diagramm zeigt. |
x | Feld für Beschriftung | Feldname jeder Zeile für die Beschriftung. |
y | Feld für Wert | Feldname jeder Zeile für den Zahlenwert. |
kind | Art | Darstellung des Diagramms. Werte: bar (Balken), line (Linie). Neu im Builder: "bar". |
height | Höhe (px) | Höhe in Pixeln. |
color | nur im Code | Farbe der Balken bzw. der Linie. Standard: var(--mc-color-primary). |
title | nur im Code | Beschreibung des Diagramms für Screenreader. |
Ein schlichtes SVG-Diagramm. Beschriftungen kommen aus dem Feld x (Standard label), Werte aus y (Standard value).
Ausdrücke {{…}}
Ausdrücke setzen Werte aus dem State oder dem Kontext ein. Sie sind reine Pfade: kein Rechnen, keine Vergleiche, keine Funktionsaufrufe. Alles, was mehr braucht, gehört in einen Handler.
{{state.profil.firstName}} Wert aus dem State
{{state.termine[0].title}} Listeneintrag über den Index (auch state.termine.0.title)
{{item.name}} Eintrag einer Liste oder angeklickte Tabellenzeile
{{event.value}} Wert eines change- oder submit-Ereignisses
{{context.organisation.name}} Kontext des Hosts
Was verfügbar ist:
| Name | Inhalt |
|---|---|
state | der ganze State der App |
state.$loading | je State-Schlüssel einer Bindung true, solange sie lädt, z. B. {{state.$loading.termine}} |
context | vom Host: organisation (id, name), user (id, firstName), theme (light/dark), locale (de/en), target (dashboard/mobile) |
item, index | in Kindern einer List und in Aktionen von rowClick bzw. click einer Liste |
event | in Aktionen: die Nutzlast des Ereignisses |
Zwei Regeln bestimmen das Ergebnis:
- Steht der Ausdruck allein, bekommst du den Wert selbst – eine Liste bleibt eine Liste, eine Zahl eine Zahl. So übergibst du
{{state.termine}}an eine Tabelle. - Steht er in einem Text, wird er zu Text:
"Hallo {{state.profil.firstName}}!". Objekte erscheinen dann als JSON, fehlende Werte als leerer Text.
Pfade auf __proto__, prototype oder constructor liefern immer nichts.
Datenbindungen
Eine Bindung lädt eine Datenquelle beim Start der App und legt das Ergebnis im State ab:
"bind": [
{ "source": "me.events", "to": "termine", "pick": "items" },
{ "source": "storage.list", "params": { "collection": "notizen", "scope": "org" }, "to": "notizen", "pick": "items" }
]
| Feld | Pflicht | Bedeutung |
|---|---|---|
source | ja | Name der Datenquelle, z. B. me.profile |
params | nein | Parameter der Quelle; Werte dürfen {{…}} enthalten |
to | ja | State-Schlüssel, der das Ergebnis bekommt. Punkte trennen Ebenen: daten.termine. Muss mit einem Buchstaben, _ oder $ beginnen. |
pick | nein | Pfad im Ergebnis, der gespeichert wird, z. B. items bei Listen oder card beim Ausweis |
Alle Bindungen aller Knoten starten gleichzeitig, sobald die App geladen ist. Die Parameter werden einmal gegen den Start-State ausgewertet. Danach laufen die load-Aktionen des Wurzelknotens. Schlägt eine Bindung fehl, zeigt die App einen roten Hinweis; die übrigen laufen weiter.
Um Daten später neu zu laden – etwa nach dem Speichern –, nimmst du die Aktion call mit demselben to.
Ereignisse
| Ereignis | Im Builder | Bausteine | event |
|---|---|---|---|
load | Beim Laden | Stack | – |
click | Beim Klick | Button, List | bei List der angeklickte Eintrag, sonst – |
change | Bei Änderung | TextInput, Select, Checkbox, DateInput, Tabs | { value }, bei Tabs der Index |
submit | Beim Absenden (Enter) | TextInput | { value } |
rowClick | Beim Klick auf eine Zeile | Table | die angeklickte Zeile |
load wirkt nur am Wurzelknoten. Ein Ereignis führt seine Aktionen nacheinander aus; wirft eine davon einen Fehler, bricht die Kette ab und die App zeigt „Aktion fehlgeschlagen: …“.
Aktionen
| Aktion | Im Builder | Zweck |
|---|---|---|
call | Daten abrufen | eine Datenquelle aufrufen, das Ergebnis optional im State ablegen |
export | Exportieren | eine Liste als CSV, XLSX oder PDF herunterladen bzw. teilen |
navigate | Navigieren | den Host zu einer seiner Seiten schicken |
setState | State setzen | einen State-Schlüssel setzen |
toast | Hinweis anzeigen | einen Hinweis im Host zeigen |
script | Skript-Handler (main.js) | einen Handler aus main.js aufrufen |
call – Daten abrufen
{ "type": "call", "source": "storage.put",
"params": { "collection": "notizen", "key": "{{state.neu.key}}", "value": "{{state.neu.text}}" } }
| Feld | Pflicht | Bedeutung |
|---|---|---|
source | ja | Datenquelle |
params | nein | Parameter, mit Ausdrücken |
to | nein | State-Schlüssel für das Ergebnis; ohne to wird das Ergebnis verworfen |
pick | nein | Pfad im Ergebnis |
export – Exportieren
{ "type": "export", "format": "csv", "data": "{{state.termine}}", "filename": "termine-{{context.organisation.name}}",
"columns": [{ "key": "title", "label": "Termin" }, { "key": "start", "label": "Beginn" }], "excel": true }
| Feld | Pflicht | Bedeutung |
|---|---|---|
format | ja | csv, xlsx oder pdf |
data | ja | Ausdruck, der eine Liste ergibt |
filename | ja | Dateiname, die Endung ergänzt die Runtime |
columns | nein | Spalten mit key und optional label; ohne Angabe alle Felder der ersten Zeile |
excel | nein | nur CSV: Semikolon und BOM für deutsches Excel (Standard) |
title | nein | nur PDF: Überschrift über der Tabelle |
Die Datei geht über die Bridge an den Host, der sie herunterlädt (Dashboard) oder teilt (Handy).
navigate – Navigieren
{ "type": "navigate", "path": "/apps" }
Bittet den Host, zu einer seiner eigenen Seiten zu wechseln – ein Pfad des Dashboards bzw. der Mitglieder-App, keine fremde Adresse. Ziele, die es in der Mitglieder-App nicht gibt, lehnt sie mit einem Fehler ab. Außerhalb eines Hosts setzt die Runtime nur den Hash der Seite.
setState – State setzen
{ "type": "setState", "key": "auswahl", "value": "{{item}}" }
value darf ein Ausdruck oder ein fester Wert sein, auch ein Objekt. Ohne value wird der Schlüssel geleert.
toast – Hinweis anzeigen
{ "type": "toast", "message": "{{state.notizen.length}} Notizen geladen", "color": "green" }
color: green, blue, orange oder red. Der Host zeigt den Hinweis in seinem eigenen Stil.
script – Handler aus main.js
{ "type": "script", "handler": "speichern", "args": { "collection": "notizen" } }
Ruft die gleichnamige exportierte Funktion aus main.js auf. args wird vorher ausgewertet. Mehr unter main.js & Handler.
Beispiel: Pinnwand
Eine App, die Notizen des Vereins im App-Speicher ablegt und anzeigt – ganz ohne main.js:
{
"schemaVersion": 1,
"name": "Pinnwand",
"auth": { "clientId": "cl_7d2e41a9", "scopes": ["mc.apps.storage:read", "mc.apps.storage:write"] },
"initialState": { "neu": "" },
"root": {
"id": "root",
"type": "Stack",
"bind": [{ "source": "storage.list", "params": { "collection": "pinnwand" }, "to": "notizen", "pick": "items" }],
"children": [
{ "id": "titel", "type": "Heading", "props": { "text": "Pinnwand {{context.organisation.name}}", "level": 2 } },
{
"id": "zeile",
"type": "Stack",
"props": { "direction": "row", "gap": 8 },
"children": [
{ "id": "eingabe", "type": "TextInput", "props": { "label": "Neue Notiz", "model": "neu" } },
{
"id": "anheften",
"type": "Button",
"props": { "label": "Anheften" },
"on": {
"click": [
{ "type": "call", "source": "storage.put",
"params": { "collection": "pinnwand", "key": "{{state.neu}}", "value": "{{state.neu}}" } },
{ "type": "call", "source": "storage.list", "params": { "collection": "pinnwand" }, "to": "notizen", "pick": "items" },
{ "type": "setState", "key": "neu", "value": "" }
]
}
}
]
},
{
"id": "liste",
"type": "List",
"props": { "data": "{{state.notizen}}", "itemText": "{{item.value}}", "empty": "Noch nichts angeheftet" }
}
]
}
}
Der Schlüssel ist hier der Text selbst – das hält das Beispiel kurz, erlaubt aber nur Zeichen aus A–Z a–z 0–9 _ -. Eine echte Pinnwand erzeugt Schlüssel in einem Handler.
Validierung
Die Runtime prüft app.json beim Laden, das Studio schon beim Tippen. Typische Meldungen:
| Meldung | Ursache |
|---|---|
schemaVersion must be 1 | falsche oder fehlende Version |
root (x): duplicate id "x" | zwei Knoten mit derselben id |
unknown component type "Foo" | Tippfehler im type |
root.children[0]: a tab needs props.label | ein Tab ohne Beschriftung |
bind[0]: invalid state key "1termine" | to beginnt mit einer Ziffer oder enthält Leerzeichen |
on.click[0]: unknown action type "open" | Aktion gibt es nicht |
invalid handler name "mein-handler" | Handler-Namen folgen den Regeln für JavaScript-Bezeichner |
Ältere Dokumente repariert das Studio beim Öffnen selbst: leere State-Schlüssel bekommen einen lesbaren Namen, Tabs ohne Titel heißen „Tab 1“, „Tab 2“ usw.