Skip to content

Alt om WordPress, webutvikling — og mer til

📤 Sende inn skjemaer i headless WordPress: REST API for Contact Form 7 og Gravity Forms

📤 Sende inn skjemaer i headless WordPress: REST API for Contact Form 7 og Gravity Forms

Du bygger et nettsted på WordPress, og et kontaktskjema er allerede på plass. Utvidelser som Contact Form 7 gir deg ferdig HTML, validering, innsendingslagring og dusinvis av integrasjoner. Klikk «Installer», lim inn shortkoden, så er du ferdig på to minutter.

Men alt endrer seg når WordPress blir et hodeløst CMS. Du er ansvarlig for hele frontenden: React, Vue, ren HTML/JS. Og skjemautvidelsen som tidligere genererte markup for deg, kontrollerer ikke lenger klientsiden. REST-API-et dens er imidlertid fortsatt der. Bare send en POST til riktig endepunkt, så har du fortsatt all kraften fra utvidelsen (feltvalidering, lagring, integrasjoner) til rådighet.

I praksis kan du også dekke rent «tradisjonelle» tilfeller via skjemautvidelsenes REST-API. La oss si at du bygger et tilpasset tema med Tailwind, og CF7s fastlåste markup med sin rigide klassestruktur ser malplassert ut. Ved å sende inn via API-et kan du kontrollere hver eneste piksel av skjemaet uten å forlate utvidelsens etablerte økosystem.

💡 Rask oversikt:

  • Hvilke endepunkter Contact Form 7 og Gravity Forms tilbyr, og hvordan du aktiverer dem i et hodeløst miljø.
  • Hvilket format du skal bruke når du sender felter, slik at utvidelsen korrekt aksepterer dataene og returnerer et resultat.
  • Hvordan du bygger et HTML-skjema, kobler på en fetch-forespørsel og viser brukeren en suksessmelding eller valideringsfeil.
  • Hvordan du forener de ulike responsformatene fra CF7 og Gravity Forms til én enkel, praktisk struktur.

Det du trenger å vite om endepunkter

Å sende data via REST-API-et er den teknisk enkle delen. Begge utvidelsene forventer en POST til et endepunkt der det dynamiske URL-segmentet er identifikatoren for det spesifikke skjemaet.

Contact Form 7 tilbyr et REST-API umiddelbart etter aktivering. Endepunktet ser slik ut:

1https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback

Fra og med versjon 5.8 (august 2023) gikk Contact Form 7 over til SHA-1-hashede skjemaidentifikatorer. Gamle numeriske ID-er fungerer fortsatt, men for nye skjemaer må du hente identifikatoren fra URL-en til skjemaredigeringssiden i administrasjonspanelet (det siste segmentet etter post=). Per april 2026 har utvidelsen over 10 millioner aktive installasjoner og er testet opp til WordPress 7.0.

Gravity Forms bruker REST API v2 (tilgjengelig siden versjon 2.4):

1https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions

Viktig merknad: Gravity Forms REST-API er deaktivert som standard. For å aktivere det, gå til utvidelsesinnstillingene → REST API-fanen → kryss av for «Aktiver tilgang til API-et». En API-nøkkel er ikke nødvendig for endepunktet for skjemainnsending; det er offentlig av design. Skjemaidentifikatoren i Gravity Forms er numerisk og synlig i administrasjonspanelet under redigering.

Struktur på forespørselsbrødteksten

La oss ta et eksempelskjema med fem felter: obligatorisk tekst, e-post og dato (før 4. oktober 1957), et valgfritt tekstområde og en obligatorisk avkrysningsboks.

Eksempel på kontaktskjema med fem inndatafelt

Contact Form 7 forventer nøkler i formatet definert gjennom form tag-syntaks. Nøkkelen samsvarer med name-attributtet til det tilsvarende feltet i HTML:

1{
2 "somebodys-name": "Marian Kenney",
3 "any-email": "[email protected]",
4 "before-space-age": "1922-03-11",
5 "optional-message": "",
6 "fake-terms": "1"
7}

Gravity Forms bruker en annen tilnærming: automatisk genererte, inkrementelle identifikatorer med prefikset input_. Felt-ID-en er synlig rett i admin-panelet når du redigerer et spesifikt felt.

Redigerer et Gravity Forms-felt med den synlige identifikatoren input_3

For det samme skjemaet ser forespørselsbodyen for Gravity Forms annerledes ut:

1{
2 "input_1": "Marian Kenney",
3 "input_2": "[email protected]",
4 "input_3": "1922-03-11",
5 "input_4": "",
6 "input_5_1": "1"
7}

Hovedpoenget: hvis du gir HTML-inputene dine name-attributter som samsvarer med pluginens forventede nøkler, skjer mappingen automatisk, og FormData vil samle dataene i riktig format uten manuell mapping.

Bygge HTML-en og sende forespørselen

For Contact Form 7 ser HTML-markupen slik ut (merk at action er endepunktet, og feltets name-attributter samsvarer med nøklene over):

1<form action="https://your-site.tld/wp-json/contact-form-7/v1/contact-forms/<FORM_ID>/feedback" method="post">
2 <label for="somebodys-name">Your name</label>
3 <input id="somebodys-name" type="text" name="somebodys-name" required>
4
5 <label for="any-email">Email</label>
6 <input id="any-email" type="email" name="any-email" required>
7
8 <label for="before-space-age">Date</label>
9 <input id="before-space-age" type="date" name="before-space-age" max="1957-10-04" required>
10
11 <label for="optional-message">Message</label>
12 <textarea id="optional-message" name="optional-message"></textarea>
13
14 <label>
15 <input type="checkbox" name="fake-terms" value="1" required>
16 I accept the terms
17 </label>
18
19 <button type="submit">Submit</button>
20</form>

For Gravity Forms er det bare action og name-attributtene som endres:

1<form action="https://your-site.tld/wp-json/gf/v2/forms/<FORM_ID>/submissions" method="post">
2 <label for="input_1">Your name</label>
3 <input id="input_1" type="text" name="input_1" required>
4 <!-- ... -->
5</form>

Så til innsending via JavaScript: FormData samler verdier etter name automatisk, så ingen mapping er nødvendig:

1const formSubmissionHandler = (event) => {
2 event.preventDefault();
3
4 const formElement = event.target;
5 const { action, method } = formElement;
6 const body = new FormData(formElement);
7
8 fetch(action, { method, body })
9 .then((response) => response.json())
10 .then((response) => {
11 if (isFormSubmissionError(response)) {
12 // Handle validation errors
13 handleValidationErrors(response);
14 return;
15 }
16 // Successful submission
17 handleSuccess(response);
18 })
19 .catch((error) => {
20 // Network error or server unavailable
21 handleNetworkError(error);
22 });
23};
24
25const formElement = document.querySelector("form");
26formElement.addEventListener("submit", formSubmissionHandler);

Dataene er sendt. Men det er ikke nok for brukeren; de trenger tilbakemelding: en suksessmelding, utheving av felt med feil, en global varsling. Heldigvis returnerer begge pluginene denne informasjonen i responsen.

Validering: serveren bestemmer, klienten viser

Utover innebygd HTML5-validering (attributter som required, type="email" og max), er det fornuftig å stole på den serverbaserte regelsjekken som pluginene tilbyr. Hvorfor: reglene er konfigurert sentralt i WordPress-admin, og å duplisere dem på klientsiden betyr dobbelt arbeid og en kilde til inkonsistens.

Både Contact Form 7 og Gravity Forms returnerer valideringsfeil direkte i responsbodyen. For komplekse scenarioer (betingede felt, avhengig validering) er det spesielt fordelaktig å basere seg på servervalidering: du slipper å synkronisere logikk mellom frontend og plugin-innstillinger.

Oppgaven koker ned til tre steg: parse JSON-responsen, trekk ut feilmeldinger, og sett dem inn i DOM-en ved siden av de tilsvarende feltene.

Responsformater og normalisering

Contact Form 7-respons ved valideringsfeil:

1{
2 "into": "#",
3 "status": "validation_failed",
4 "message": "One or more fields have an error. Please check and try again.",
5 "posted_data_hash": "",
6 "invalid_fields": [
7 {
8 "into": "span.wpcf7-form-control-wrap.somebodys-name",
9 "message": "The field is required.",
10 "idref": null,
11 "error_id": "-ve-somebodys-name"
12 }
13 ]
14}

Ved suksess er responsen mer kompakt:

1{
2 "into": "#",
3 "status": "mail_sent",
4 "message": "Thank you for your message. It has been sent.",
5 "posted_data_hash": "d52f9f9de995287195409fe6dcde0c50"
6}

Gravity Forms sitt svar ved valideringsfeil er strukturert annerledes:

1{
2 "is_valid": false,
3 "validation_messages": {
4 "1": "This field is required.",
5 "2": "This field is required.",
6 "3": "This field is required.",
7 "5": "This field is required."
8 },
9 "page_number": 1,
10 "source_page_number": 1
11}

Og et vellykket svar inneholder bekreftelse pakket inn i HTML:

1{
2 "is_valid": true,
3 "page_number": 0,
4 "source_page_number": 1,
5 "confirmation_message": "<div>Thanks for contacting us! We will get in touch with you shortly.</div>",
6 "confirmation_type": "message"
7}

Forskjellen i tilnærming er åpenbar: CF7 bygger inn feil i en matrise med objekter med CSS-selektorer, mens Gravity Forms bruker et flatt objekt med numeriske nøkler uten input_-prefikset. Suksessmeldingen fra Gravity Forms kommer pakket inn i HTML. Feltnøkler i CF7-svar er innebygd i selektorer (f.eks. span.wpcf7-form-control-wrap.somebodys-name) og krever uttrekk via regex.

I stedet for å forgrene logikken for hver plugin, er det mer praktisk å normalisere begge svarene til et enhetlig format:

1{
2 "isSuccess": false,
3 "message": "One or more fields have an error. Please check and try again.",
4 "validationError": {
5 "somebodys-name": "The field is required.",
6 "any-email": "The field is required.",
7 "input_3": "The field is required.",
8 "input_5": "This field is required."
9 }
10}

Ved suksess settes isSuccess til true, og validationError er et tomt objekt.

Normaliseringskode for Contact Form 7:

1const normalizeContactForm7Response = (response) => {
2 const isSuccess = response.status === 'mail_sent';
3 const message = isSuccess
4 ? response.message
5 : response.message || 'One or more fields have an error.';
6
7 const validationError = isSuccess
8 ? {}
9 : Object.fromEntries(
10 response.invalid_fields.map((error) => {
11 const key = /cf7[-a-z]*.(.*)/.exec(error.into)[1];
12 return [key, error.message];
13 })
14 );
15
16 return { isSuccess, message, validationError };
17};

Normaliseringskode for Gravity Forms (merk: feilnøkler får lagt til input_-prefikset slik at de samsvarer med forespørselsnøklene):

1const normalizeGravityFormsResponse = (response) => {
2 const isSuccess = response.is_valid;
3 const message = isSuccess
4 ? stripHtml(response.confirmation_message)
5 : 'There was a problem with your submission.';
6
7 const validationError = isSuccess
8 ? {}
9 : Object.fromEntries(
10 Object.entries(response.validation_messages).map(([key, value]) => [
11 `input_${key}`,
12 value,
13 ])
14 );
15
16 return { isSuccess, message, validationError };
17};

Nå har du et enhetlig svarobjekt uavhengig av plugin. Det eneste som gjenstår er å skrive feilvisningen og klassevekslingen på DOM-elementer, så er skjemaet klart til bruk.

Fra normalisering til et levende grensesnitt

Når svaret er konvertert til en enhetlig struktur, handler visning av tilbakemelding om DOM-manipulasjon. Å legge til en feilmelding ved siden av et felt, veksle en klasse på en wrapper og vise en global varsling: disse tre handlingene er nok for de aller fleste scenarioer.

For reaktive grensesnittoppdateringer er lette deklarative biblioteker som Alpine.js praktiske. Minimal syntaks, intet byggetrinn og naturlig integrasjon med serversvar gjør det til et praktisk valg for skjemaer i et hodeløst miljø. Alpine.js-tilnærmingen ble dekket i detalj på CSS-Tricks; koden fra det materialet fungerer nesten uendret med det normaliserte svaret vi fikk ovenfor.

Bunnlinjen

Å gjenskape funksjonaliteten på klientsiden som skjemautvidelser gir «ut av boksen», er et par timers arbeid for enkle skjemaer. En fin bonus: ved å abstrahere responsen gjennom en normaliseringsfunksjon får du en utskiftbar backend. Å bytte fra Contact Form 7 til Gravity Forms (eller omvendt) kan gjøres uten endringer i frontend; bare bytt ut endepunktet og normaliseringsfunksjonen.

Flersides skjemaer, forhåndsvisning av opplastede bilder, priskalkulatorer: ja, det er betydelig utviklingsarbeid. Men jo mer unike kravene til et prosjekt er, desto sterkere er argumentet for en skreddersydd frontend oppå REST API-et: du kjemper ikke mot andres markup eller jobber rundt begrensningene i forhåndsbygd rendering.

Den hodeløse tilnærmingen til skjemaer er ikke en hypotetisk fremtid. I dag tilbyr utvidelser som Contact Form 7 og Gravity Forms fullverdige REST API-er, og frontend-rammeverk lar deg bygge et skjema på timer i stedet for dager. Prøv det på ditt neste prosjekt der skjemaets utseende er kritisk: bruk CF7 eller GF som backend og bygg grensesnittet fra bunnen av. Du vil sannsynligvis bli overrasket over hvor enkelt det er.

⁉️🤔 Ofte stilte spørsmål

Fungerer Contact Form 7 REST API-et med gratisversjonen?

Ja, REST API-et er tilgjengelig umiddelbart etter aktivering av den gratis utvidelsen; ingen ytterligere konfigurasjon er nødvendig. Per april 2026 har Contact Form 7 over 10 millioner aktive installasjoner, og REST API-et har vært en stabil del av utvidelsens kjerne siden versjon 4.8.

Hva er annerledes med hashede skjema-ID-er i nyere versjoner av Contact Form 7?

Fra og med versjon 5.8 (august 2023) genererer CF7 en SHA-1-hash som skjemaidentifikator i stedet for en numerisk ID. Gamle numeriske ID-er fungerer fortsatt. Du finner hashen i URL-en til skjemaredigeringssiden i administrasjonspanelet: /wp-admin/admin.php?page=wpcf7&post=<HASH>&action=edit. Den settes inn i endepunktet på samme måte som en numerisk ID.

Kreves det en API-nøkkel for å sende inn skjemaer via Gravity Forms REST API?

Nei, endepunktet /gf/v2/forms/<ID>/submissions krever ikke autentisering for innsending. Selve Gravity Forms REST API-et er imidlertid deaktivert som standard; du må aktivere det i utvidelsens innstillinger (Skjemaer → Innstillinger → REST API → Aktiver tilgang til API-et).

Kan den samme JavaScript-koden brukes for både Contact Form 7 og Gravity Forms?

Ja, det er nettopp det responsnormalisering er til for. Begge normaliseringsfunksjonene (for CF7 og GF) returnerer et objekt med samme struktur: feltene isSuccess, message og validationError. Koble til den aktuelle funksjonen avhengig av utvidelsen, så vil all den resterende koden (feilvisning, feltutheving, global varsling) fungere uten endringer.

Hva bør jeg gjøre hvis skjemaet ikke vil sendes inn og serveren returnerer en 404?

Sjekk tre ting: om utvidelsens REST API er aktivert (spesielt relevant for Gravity Forms), om skjemaidentifikatoren i endepunktets URL er korrekt, og om REST API-et blokkeres på servernivå eller av en sikkerhetsutvidelse. For Contact Form 7, sørg også for at WordPress REST API er globalt aktivt; uten det vil ikke CF7 kunne behandle AJAX-innsendinger.