Formulare & Form Builder
Erstelle Formulare mit dem visuellen Form Builder und verbinde sie mit eigenen Blöcken, indem du die Formular-ID in einem Block-Feld speicherst.
Überblick
Cmssy hat einen eingebauten Form Builder, mit dem du Formulare visuell erstellst und mit jedem eigenen Block verbindest. Formulare übernehmen Validierung, Einsendungen, E-Mail-Benachrichtigungen und per Webhook ausgelieferte Events — deine Blöcke müssen nur die UI rendern.
Das System besteht aus zwei Teilen:
- Form Builder (Dashboard > Forms) — Felder, Validierung und Aktionen definieren
- Ein
formId-Feld auf deinem Block — speichert, welches Formular der Block rendern soll
Ein Formular erstellen
1. Öffne den Form Builder
Gehe zu Dashboard → Forms → Create Form. Gib ihm einen Namen und einen Slug (z. B. contact-form).
2. Felder hinzufügen
Jedes Feld hat:
| Eigenschaft | Beschreibung |
|---|---|
| name | Eindeutiger Feld-Schlüssel (z. B. email, message) |
| type | text, email, textarea, number, phone, url, date, select, multiselect, checkbox, radio, file, hidden |
| label | Anzeigelabel (mehrsprachig) |
| placeholder | Platzhaltertext (mehrsprachig) |
| validation | required, minLength, maxLength, minValue, maxValue, pattern, customMessage |
| width | full, half oder third — steuert die Feldbreite im Formular-Grid |
| options | Für select, multiselect, radio — Array aus { value, label } |
| order | Anzeigereihenfolge |
3. Einstellungen konfigurieren
| Einstellung | Beschreibung |
|---|---|
| Action Type | contact oder custom — mehr wird nicht akzeptiert. Keiner von beiden ändert, was der Server mit einer Einsendung tut; es ist ein Label, an dem dein Block ein Layout wählen kann. |
| Email Recipients | Liste der E-Mails, die Formular-Benachrichtigungen erhalten |
| Success Message | Wird nach erfolgreicher Einsendung angezeigt (mehrsprachig) |
| Error Message | Wird bei Fehlern angezeigt (mehrsprachig) |
| Submit Button Label | Button-Text (mehrsprachig) |
| Save Submissions | Einsendungen in der Datenbank speichern (Standard: true) |
| Send Email Notification | E-Mail an Empfänger bei jeder Einsendung (Standard: true) |
| Require Login | Nur eingeloggte Nutzer können absenden |
4. Veröffentlichen
Setze den Status auf Published. Kopiere seine Formular-ID — du referenzierst sie aus deinem Block.
Formulare in eigenen Blöcken nutzen
Ein Formular referenzieren
Füge deinem Block-Schema ein formId-Feld hinzu. Nimm fields.form(): Es rendert im Editor einen Formular-Picker und speichert die ID des gewählten Formulars - niemand muss eine ID von Hand kopieren.
// blocks/contact/block.ts
import { defineBlock, fields } from "@cmssy/react";
import Contact from "./Contact";
export const contactBlock = defineBlock({
type: "contact",
label: "Contact",
component: Contact,
props: {
formId: fields.form({ label: "Formular" }),
submitLoadingText: fields.text({ label: "Loading Text", defaultValue: "Sending..." }),
successHeading: fields.text({ label: "Success Heading", defaultValue: "Message Sent!" }),
},
});Das Formular rendern
Das SDK löst jedes von einem Block-formId referenzierte Formular automatisch auf und injiziert die Definition in den Block-context als context.forms[formId] — kein Fetching nötig:
// blocks/contact/src/Contact.tsx
export default function Contact({ content, context }) {
const { formId } = content;
const formDef = formId ? context?.forms?.[formId] ?? null : null;
// formDef.fields - array of field definitions
// formDef.settings - submit label, messages, action type
// ...render the fields
}Das Formular absenden
Das Absenden läuft über public.form.submit. Halte die Mutation in deinem eigenen Repo und schicke sie mit deinem konfigurierten Client - das SDK führt denselben String als SUBMIT_FORM_MUTATION, aber unter @cmssy/core/internal, und das ist keine öffentliche API. Das Backend validiert die Felder, speichert die Einsendung, verschickt die Benachrichtigungs-Mails, feuert form.submitted und antwortet mit Erfolg oder Fehler:
// blocks/contact/actions.ts
"use server";
import { createCmssyClient, type CmssyFormSubmitResponse } from "@cmssy/react";
import { cmssy } from "@/cmssy.config";
const SUBMIT_FORM = `mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
public {
form {
submit(formId: $formId, input: $input) {
success
message
submissionId
redirectUrl
}
}
}
}`;
const client = createCmssyClient(cmssy);
export async function submitForm(formId: string, data: Record<string, string>) {
const res = await client.query<{
public: { form: { submit: CmssyFormSubmitResponse } };
}>(SUBMIT_FORM, { formId, input: { data } });
return res.public.form.submit; // { success, message, submissionId, redirectUrl }
}Das Absenden ist der einzige Schreibvorgang, den ein öffentliches Frontend ohne Token ausführen kann. Alles andere rund um Formulare - anlegen, Einsendungen lesen, Status ändern - braucht einen autorisierten Client.
Referenz-Formularblöcke
Cmssy liefert keine Blöcke mit — im Headless-Modell baust du Blöcke mit dem SDK in deinem eigenen Repo. Das sind gängige Formularblöcke, die du als Referenzmuster implementieren kannst; jeder verbindet ein Form-Builder-Formular (seinen actionType) mit einem Block, den du renderst:
| Block | Form Action | Beschreibung |
|---|---|---|
| Contact | contact | Kontaktformular mit Info-Karten und Zitat |
| Newsletter | custom | E-Mail-Anmeldung, die ein Workspace-Webhook an deinen Mailing-Anbieter weitergibt |
| Angebotsanfrage | contact | Längere Anfrage, die im Submissions-Tab landet und dir gemailt wird |
Jeder definiert das Formular serverseitig im Form Builder und rendert es über ein formId-Feld in einem Block — das Markup gehört dir als Dev. Siehe Mitglieder-Authentifizierung für die Auth-Flows.
Login, Registrierung und Passwort-Reset sind keine Form-Builder-Formulare: sie rufen direkt die Mitglieder-Auth-Mutationen auf (login, register, forgotPassword, resetPassword).
Formular-Einsendungen
Einsendungen ansehen
Gehe zu Dashboard → Forms → [Dein Formular] → Submissions. Jede Einsendung zeigt:
- Alle Feldwerte
- Status (
pending,processed,spam,archived) - IP-Adresse, User Agent, Referrer
- Zeitstempel
- E-Mail-Zustellstatus
Lebenszyklus einer Einsendung
- Nutzer sendet das Formular auf deiner veröffentlichten Seite ab
- Der Server validiert die Felder und prüft Rate-Limits
- Die Einsendung wird mit Status
pendinggespeichert - E-Mail-Benachrichtigung an Empfänger (falls aktiviert)
form.submittedwird gefeuert — nach dem Speichern der Einsendung, an jeden Workspace-Webhook, der auf dieses Event abonniert ist, und ein Fehlschlag lässt die Einsendung nie scheitern- Der Status bleibt
pending, bis du ihn im Submissions-Tab änderst
Eine Einsendung in ein externes System bringen
Registriere einen Workspace-Webhook auf das Event form.submitted. Das ist der einzige Weg nach draußen, und er ist der belastbare: signiert, mit eigener Retry-Leiter, von einem Sweeper aufgefangen und sichtbar in einem Zustellprotokoll, das du lesen und erneut abspielen kannst. Ein Formular hat keine eigene Webhook-Einstellung.
Das Event trägt die Einsendungs-ID, nicht die Antworten. Die liest du mit get_form_submission oder form.submission(submissionId:) über die API, auf derselben Permission forms:submissions:view, die einen Endpunkt überhaupt zum Abonnieren berechtigt. So bleiben die Antworten an einem Ort mit einem Löschpfad, und keine große Einsendung schiebt ein Event über das Zustell-Größenlimit und bringt es für alle anderen Abonnenten zum Schweigen.
Spam-Schutz
Formulare bringen eingebautes Rate-Limiting mit:
- Pro-IP-Limit pro Formular
- Honeypot-Felder lassen sich als Feldtyp
hiddenhinzufügen
Referenz der Action Types
contact
Speichert die Einsendung + sendet eine E-Mail an die konfigurierten Empfänger. Der Standard für die meisten Formulare.
custom
Nur ein Label: der Server behandelt ein custom-Formular genau wie ein contact-Formular. Nutze es, um Formulare zu markieren, die dein Block anders rendert. Für einen externen Dienst (Zapier, Slack, ein CRM) abonniere einen Workspace-Webhook auf form.submitted.
Login und Registrierung von Mitgliedern sind keine Formular-Action-Types — siehe Mitglieder-Authentifizierung.
Tipps
- Veröffentliche dein Formular immer, bevor du es referenzierst — Draft-Formulare können nicht aufgelöst werden
- Nutze die Breiten
halfundthirdfür mehrspaltige Layouts (z. B. Vorname + Nachname nebeneinander) - Mehrsprachige Labels — Formularfelder unterstützen Labels und Platzhalter pro Sprache, passend zum Sprachsystem der Site
- Test-Einsendungen werden wie echte gespeichert — lösche sie danach im Submissions-Tab
- Webhook-Debugging — öffne Settings → Webhooks → Deliveries: jeder Versuch steht dort mit seinem Response-Code, und du kannst einen erneut abspielen