Skip to content

Allt om WordPress, webbutveckling — och mer därtill

📤 Skicka formulär i headless WordPress: REST API för Contact Form 7 och Gravity Forms

📤 Skicka formulär i headless WordPress: REST API för Contact Form 7 och Gravity Forms

Du bygger en sajt på WordPress, och ett kontaktformulär är redan fixat. Tillägg som Contact Form 7 ger dig färdig HTML, validering, lagring av inskick och dussintals integrationer. Klicka på "Installera", klistra in shortcoden, så är du klar på två minuter.

Men allt förändras när WordPress blir ett headless CMS. Du ansvarar för hela frontenden: React, Vue, ren HTML/JS. Och formulärtillägget som tidigare renderade märkspråk åt dig kontrollerar inte längre klientsidan. Dess REST-API finns dock kvar. Skicka bara en POST till rätt endpoint, så har du fortfarande all kraft från tillägget (fältvalidering, lagring, integrationer) till ditt förfogande.

I praktiken kan du också täcka rent "traditionella" fall via formulärtilläggens REST API. Anta att du bygger ett anpassat tema med Tailwind, och CF7:s fasta märkspråk med sin rigida klass-struktur ser malplacerat ut. Genom att skicka via API:t kan du styra varje pixel i formuläret utan att överge tilläggets etablerade ekosystem.

💡 Snabb översikt:

  • Vilka endpoints Contact Form 7 och Gravity Forms tillhandahåller och hur du aktiverar dem i en headless-miljö.
  • Vilket format du ska använda när du skickar fält så att tillägget korrekt tar emot datan och returnerar ett resultat.
  • Hur du bygger ett HTML-formulär, kopplar på en fetch-förfrågan och visar ett bekräftelsemeddelande eller valideringsfel för användaren.
  • Hur du förenar de olika svarsformaten från CF7 och Gravity Forms till en enda praktisk struktur.

Vad du behöver veta om endpoints

Att skicka data via REST API:t är den tekniskt enkla delen. Båda tilläggen förväntar sig en POST till en endpoint där det dynamiska URL-segmentet är identifieraren för det specifika formuläret.

Contact Form 7 tillhandahåller ett REST API direkt efter aktivering. Endpointen ser ut så här:

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

Från och med version 5.8 (augusti 2023) övergick Contact Form 7 till SHA-1-hashade formuläridentifierare. Gamla numeriska ID:n fungerar fortfarande, men för nya formulär måste du hämta identifieraren från URL:en på formulärets redigeringssida i adminpanelen (det sista segmentet efter post=). I april 2026 har tillägget över 10 miljoner aktiva installationer och är testat upp till WordPress 7.0.

Gravity Forms använder REST API v2 (tillgängligt sedan version 2.4):

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

Viktig notering: Gravity Forms REST API är inaktiverat som standard. För att aktivera det, gå till tilläggets inställningar → REST API-fliken → kryssa i "Aktivera åtkomst till API:et". En API-nyckel krävs inte för formulärets inskicknings-endpoint; den är publik av design. Formuläridentifieraren i Gravity Forms är numerisk och synlig i adminpanelen vid redigering.

Struktur för request body

Låt oss ta ett exempelformulär med fem fält: obligatorisk text, e-post och datum (före 4 oktober 1957), en valfri textarea och en obligatorisk kryssruta.

Exempel på kontaktformulär med fem inmatningsfält

Contact Form 7 förväntar sig nycklar i det format som definieras genom form tag-syntax. Nyckeln matchar name-attributet för motsvarande fält 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 använder en annan metod: automatiskt genererade inkrementella identifierare med prefixet input_. Fält-ID:t syns direkt i adminpanelen när du redigerar ett specifikt fält.

Redigera ett Gravity Forms-fält med den synliga identifieraren input_3

För samma formulär ser request body för Gravity Forms annorlunda 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}

Huvudpoäng: om du ger dina HTML-inputs name-attribut som matchar pluginens förväntade nycklar sker mappningen automatiskt, och FormData samlar in data i rätt format utan manuell mappning.

Bygga HTML och skicka förfrågan

För Contact Form 7 ser HTML-uppmärkningen ut så här (notera att action är endpointen, och fältens name-attribut matchar nycklarna ovan):

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>

För Gravity Forms ändras bara action och name-attributen:

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>

Nu till inskick via JavaScript: FormData samlar in värden efter name automatiskt, så ingen mappning behövs:

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);

Datan skickas. Men det räcker inte för användaren; de behöver återkoppling: ett bekräftelsemeddelande, markering av fält med fel, ett övergripande meddelande. Som tur är returnerar båda plugins denna information i svaret.

Validering: servern avgör, klienten visar

Utöver inbyggd HTML5-validering (attribut som required, type="email" och max) är det klokt att förlita sig på den regelkontroll på serversidan som plugins erbjuder. Anledningen: reglerna konfigureras centralt i WordPress admin, och att duplicera dem på klientsidan innebär dubbelarbete och en källa till inkonsekvens.

Både Contact Form 7 och Gravity Forms returnerar valideringsfel direkt i svarskroppen. För komplexa scenarier (villkorade fält, beroendevalidering) är det särskilt fördelaktigt att förlita sig på serversidans validering: du slipper synkronisera logik mellan frontend och plugin-inställningarna.

Uppgiften kokar ner till tre steg: tolka JSON-svaret, extrahera felmeddelanden och infoga dem i DOM:en intill motsvarande fält.

Svarsformat och normalisering

Contact Form 7-svar vid valideringsfel:

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}

Vid lyckad inskickning är svaret 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 svar vid valideringsfel är strukturerat annorlunda:

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}

Och ett lyckat svar innehåller bekräftelse inbäddad 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}

Skillnaden i tillvägagångssätt är tydlig: CF7 bäddar in fel i en array av objekt med CSS-selektorer, medan Gravity Forms använder ett platt objekt med numeriska nycklar utan prefixet input_. Framgångsmeddelandet från Gravity Forms levereras inlindat i HTML. Fältnycklar i CF7-svar är inbäddade i selektorer (t.ex. span.wpcf7-form-control-wrap.somebodys-name) och kräver extrahering med regex.

Istället för att förgrena logiken för varje plugin är det smidigare att normalisera båda svaren till ett enhetligt 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}

Vid framgång sätts isSuccess till true, och validationError är ett tomt objekt.

Normaliseringskod för 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};

Normaliseringskod för Gravity Forms (observera: felnycklar får prefixet input_ tillagt så att de matchar förfrågningsnycklarna):

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};

Nu har du ett enhetligt svarsobjekt oavsett plugin. Det enda som återstår är att skriva felvisningen och klassväxlingen på DOM-elementen, så är formuläret redo att användas.

Från normalisering till ett levande gränssnitt

När svaret väl har omvandlats till en enhetlig struktur handlar återkopplingsvisningen om DOM-manipulation. Att lägga till ett felmeddelande bredvid ett fält, växla en klass på en wrapper och visa ett globalt meddelande: dessa tre åtgärder räcker för de allra flesta scenarier.

För reaktiva gränssnittsuppdateringar är lätta deklarativa bibliotek som Alpine.js bekväma. Minimal syntax, inget byggsteg och naturlig integration med serversvar gör det till ett praktiskt val för formulär i en headless-miljö. Alpine.js-metoden behandlades i detalj på CSS-Tricks; koden från det materialet fungerar nästan oförändrad med det normaliserade svar vi fick fram ovan.

Slutsatsen

Att återskapa den klientfunktionalitet som formulärplugins erbjuder "out of the box" är ett par timmars jobb för enkla formulär. En trevlig bonus: genom att abstrahera svaret via en normaliseringsfunktion får du en utbytbar backend. Att byta från Contact Form 7 till Gravity Forms (eller tvärtom) kan göras utan frontendändringar; byt bara ut endpointen och normaliseringsfunktionen.

Flersidiga formulär, förhandsvisning av uppladdade bilder, prisberäknare: ja, det är seriös utveckling. Men ju mer unika ett projekts krav är, desto starkare blir argumenten för en skräddarsydd frontend ovanpå REST API:et: du slipper brottas med någon annans markup eller jobba runt begränsningarna i förbyggd rendering.

Det headless-baserade angreppssättet för formulär är inte en hypotetisk framtid. Idag erbjuder plugins som Contact Form 7 och Gravity Forms fullfjädrade REST API:er, och frontend-ramverk låter dig bygga ett formulär på timmar snarare än dagar. Testa det i ditt nästa projekt där formulärets utseende är kritiskt: använd CF7 eller GF som backend och bygg gränssnittet från grunden. Du kommer troligen att bli förvånad över hur okomplicerat det är.

⁉️🤔 Vanliga frågor

Fungerar Contact Form 7:s REST API med gratisversionen?

Ja, REST API:et är tillgängligt direkt efter aktivering av gratispluginen; ingen ytterligare konfiguration krävs. I april 2026 har Contact Form 7 över 10 miljoner aktiva installationer, och REST API:et har varit en stabil del av pluginens kärna sedan version 4.8.

Vad är skillnaden med hashade formulär-ID:n i nyare versioner av Contact Form 7?

Från och med version 5.8 (augusti 2023) genererar CF7 en SHA-1-hash som formuläridentifierare istället för ett numeriskt ID. Gamla numeriska ID:n fungerar fortfarande. Du hittar hashen i URL:en på formulärets redigeringssida i adminpanelen: /wp-admin/admin.php?page=wpcf7&post=<HASH>&action=edit. Den används i endpointen på samma sätt som ett numeriskt ID.

Krävs en API-nyckel för att skicka formulär via Gravity Forms REST API?

Nej, endpointen /gf/v2/forms/<ID>/submissions kräver ingen autentisering för inskick. Gravity Forms REST API är dock inaktiverat som standard; du måste aktivera det i pluginens inställningar (Formulär → Inställningar → REST API → Aktivera åtkomst till API:et).

Kan samma JavaScript-kod användas för både Contact Form 7 och Gravity Forms?

Ja, det är precis vad svarsnormalisering är till för. Båda normaliseringsfunktionerna (för CF7 och GF) returnerar ett objekt med samma struktur: fälten isSuccess, message och validationError. Koppla in rätt funktion beroende på plugin, så fungerar all övrig kod (felvisning, fältmarkering, global notifiering) utan ändringar.

Vad ska jag göra om formuläret inte skickas och servern returnerar en 404?

Kontrollera tre saker: om pluginens REST API är aktiverat (särskilt relevant för Gravity Forms), om formuläridentifieraren i endpoint-URL:en är korrekt, och om REST API:et blockeras på servernivå eller av ett säkerhetsplugin. För Contact Form 7, se också till att WordPress REST API är globalt aktivt; utan det kan CF7 inte bearbeta AJAX-inskick.