{"id":31820,"date":"2025-04-08T10:50:14","date_gmt":"2025-04-08T15:50:14","guid":{"rendered":"https:\/\/wpengine.com\/builders\/?p=31820"},"modified":"2025-04-08T10:50:16","modified_gmt":"2025-04-08T15:50:16","slug":"gravity-forms-in-headless-wordpress-with-nuxt-vue","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/","title":{"rendered":"Gravity Forms in Headless WordPress with Nuxt\/Vue"},"content":{"rendered":"\n<p>Gravity Forms is a WordPress plugin that allows you to create a variety of forms on your WordPress site. Its large selection of add-ons lets you send collected form data to various CRMs, process data, and more! <\/p>\n\n\n\n<p>In this article, you\u2019ll learn how you can query for Gravity Form data, render the form in a Nuxt.js app, perform field validation, and submit the form entries to your headless WordPress backend.<\/p>\n\n\n\n<p>I\u2019ll provide a <a href=\"https:\/\/github.com\/Fran-A-Dev\/nuxt3-headlesswp-gravity-forms\">Nuxt.js app repo<\/a> that contains Vue components, Vue composables, and helper functions that you can use for your own projects and experiment with. Let\u2019s dive in!&nbsp;<\/p>\n\n\n\n<div class=\"wp-block-group has-polar-background-color has-background is-layout-flow wp-container-core-group-is-layout-7a03825d wp-block-group-is-layout-flow\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-large-font-size\"><strong>Table of Contents<\/strong><\/p>\n\n\n\n<ul id=\"Prerequisites\" class=\"wp-block-list\">\n<li><a href=\"#prequisites\">Prerequisites<\/a><\/li>\n\n\n\n<li><a href=\"#wpgraphql-for-gravity-forms\">WPGraphQL for Gravity Forms<\/a><\/li>\n\n\n\n<li><a href=\"#querying-for-a-form\">Querying for a Form<\/a><\/li>\n\n\n\n<li><a href=\"#gravity-forms-field-support\">Gravity Forms Field Support<\/a><\/li>\n\n\n\n<li><a href=\"#input-type-prop\">inputType Prop<\/a><\/li>\n\n\n\n<li><a href=\"#querying-for-the-form-in-nuxt\">Querying for the Form in Nuxt<\/a><\/li>\n\n\n\n<li><a href=\"#submitting-the-form-in-nuxt\">Submitting the form in Nuxt<\/a><\/li>\n\n\n\n<li><a href=\"#rendering-the-form\">Rendering the Form<\/a><\/li>\n\n\n\n<li><a href=\"#field-component-rendering\">Field Component Rendering<\/a><\/li>\n\n\n\n<li><a href=\"#error-handling\">Error Handling<\/a><\/li>\n\n\n\n<li><a href=\"#field-validation-errors\">Field Validation Errors<\/a><\/li>\n\n\n\n<li><a href=\"#not-included\">What is Not Included<\/a><\/li>\n\n\n\n<li><a href=\"#conclusion\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>To benefit from this article, you should be familiar with the basics of working with the command line, <a href=\"https:\/\/wpengine.com\/builders\/headless-roadmap\/\">headless WordPress<\/a> development, <a href=\"https:\/\/vuejs.org\/\">Vue<\/a>, and <a href=\"https:\/\/nuxt.com\/\">Nuxt<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Steps for setting up:<\/h3>\n\n\n\n<p>1. Set up a WordPress site and get it running.&nbsp; <a href=\"https:\/\/localwp.com\/\">Local by WP Engine<\/a> is a good dev environment to use.<\/p>\n\n\n\n<p>2. Install and activate the <a href=\"https:\/\/www.gravityforms.com\/\">Gravity Forms<\/a>, <a href=\"https:\/\/www.wpgraphql.com\/\">WPGraphQL<\/a>, and <a href=\"https:\/\/github.com\/harness-software\/wp-graphql-gravity-forms\">WPGraphQL for Gravity Forms<\/a> WordPress plugins.3. Clone down <a href=\"https:\/\/github.com\/Fran-A-Dev\/nuxt3-headlesswp-gravity-forms\/tree\/main\">the Nuxt repo<\/a> for this project by copying and pasting this command in your terminal:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>npx degit Fran-A-Dev\/nuxt3-headlesswp-gravity-forms<span class=\"hljs-comment\">#main my-project<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>4. Import the questionnaire form. From the WordPress admin sidebar, go to <code>Forms > Import\/Export > Import Forms<\/code>. Select the<code> gravityforms-questionnaire-form.json<\/code> inside the root of the Nuxt project folder and click the button to import it.<\/p>\n\n\n\n<p>5. Create a <code>.env.local<\/code> file inside of the root of the Nuxt project. Open that file in a text editor and paste in :<\/p>\n\n\n\n<p><code>NUXT_PUBLIC_WORDPRESS_API_URL=http:\/\/wpgraphqlgravtyforms.local\/graphql,<\/code>replacing <code>wpgraphqlgravtyforms.local <\/code>with the domain for your WordPress site. This is the endpoint that Nuxt will use when it sends requests to your WordPress backend.<\/p>\n\n\n\n<p>6. Run <code>npm install<\/code> to install the dependencies.<\/p>\n\n\n\n<p>7. Run <code>npm run dev<\/code> to get the server running locally.<\/p>\n\n\n\n<p>8. You should now be able to click the \u201cQuestionnaire\u201d link in the header to go to the form at <a href=\"http:\/\/localhost:3000\/\">http:\/\/localhost:3000\/questionnaire<\/a> in a web browser and see it in all its glory:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB\" alt=\"\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"wpgraphql-for-gravity-forms\">WPGraphQL for Gravity Forms<\/h2>\n\n\n\n<p>The WPGraphQL for Gravity Forms plugin is a powerful extension for WPGraphQL that provides a comprehensive suite of features that allows developers to interact with Gravity Forms via GraphQL. Let\u2019s start by querying for a form.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"querying-for-a-form\">Querying for a Form<\/h3>\n\n\n\n<p>To query for a form, we have the <code>`gfForm`<\/code> query that we can use to query for data about our Gravity Forms. Here\u2019s a simple example if you want to replace the existing query in the project, open up <code>`composables\/useGravityForm.js`<\/code> and paste this query in replacement of the one currently there:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>query getForm {\n<\/span><\/span><span class='shcb-loc'><span>  gfForm(id: 1, idType: DATABASE_ID) {\n<\/span><\/span><span class='shcb-loc'><span>    databaseId\n<\/span><\/span><span class='shcb-loc'><span>    title\n<\/span><\/span><span class='shcb-loc'><span>    description\n<\/span><\/span><span class='shcb-loc'><span>    formFields(first: 500) {\n<\/span><\/span><span class='shcb-loc'><span>      nodes {\n<\/span><\/span><span class='shcb-loc'><span>        ... on TextField {\n<\/span><\/span><span class='shcb-loc'><span>          id\n<\/span><\/span><span class='shcb-loc'><span>          type\n<\/span><\/span><span class='shcb-loc'><span>          label\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on SelectField {\n<\/span><\/span><span class='shcb-loc'><span>          id\n<\/span><\/span><span class='shcb-loc'><span>          type\n<\/span><\/span><span class='shcb-loc'><span>          label\n<\/span><\/span><span class='shcb-loc'><span>          choices {\n<\/span><\/span><span class='shcb-loc'><span>            text\n<\/span><\/span><span class='shcb-loc'><span>            value\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>      }\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p><br><br>In this query, we are asking for form data.&nbsp; The <code>`gfForm`<\/code> query retrieves a specific form by its <code>database ID (id: 1)<\/code>.<\/p>\n\n\n\n<p>For that form, the query fetches basic information like its title and description.<\/p>\n\n\n\n<p>The query also fetches the <code>formFields<\/code> (up to ), and for each field, it checks whether it&#8217;s a <code>TextField<\/code> or <code>SelectField<\/code>. Depending on the type, it will fetch the appropriate data such as id, type, label, and for <code>SelectField<\/code>, it will also retrieve the choices (with their text and value).<\/p>\n\n\n\n<p>Go ahead and test out this query right from the WordPress admin by following these steps:<\/p>\n\n\n\n<p>1. Go to <code>GraphQL &gt; GraphiQL IDE<\/code>.<\/p>\n\n\n\n<p>2. Paste the query above into the left column, replacing <code>id: 1<\/code> with the ID of the imported form.<\/p>\n\n\n\n<p>3. Click the \u25b6 button to execute the query.<\/p>\n\n\n\n<p>4. See the results returned in the right column.  You should see this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXcXYXLt8NrAIeA_cQeVpkDkE41da80hZI5X4cOMrxhm3_124yK6_tv1-aJpW5jlipMU003yb_8pnrkG-TdMqiI3rOIRFYFuVgem6hS836KY4xGSyxklXZH_Y2WrRF-o-E6Py4rX?key=Vh09bUmp-N0ij2w2pffuOlVB\" alt=\"\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"gravity-forms-field-support\">Gravity Forms Field Support<\/h3>\n\n\n\n<p>Now, let\u2019s highlight one of the latest features of WPGraphQL for Gravity Forms which we use in this project.&nbsp; <a href=\"https:\/\/github.com\/AxeWP\/wp-graphql-gravity-forms\/blob\/develop\/docs\/form-field-support.md\">This feature is Forms Field support<\/a> with the <code>FormField<\/code> interface.<\/p>\n\n\n\n<p>The interface approach leverages GraphQL interfaces to abstract shared properties among Gravity Forms fields, meaning you can query a common set of fields like <code>\u201clabel\u201d<\/code> or <code>\u201cisRequired\u201d<\/code> across multiple field types.<\/p>\n\n\n\n<p>This method allows you to write a more composable query that automatically includes any new field type that implements a given interface without needing to update your query.<\/p>\n\n\n\n<p>In our project, we used inline fragments on interfaces such as <code>GfFieldWithLabelSetting<\/code> and <code>GfFieldWithRulesSetting<\/code> to fetch common properties like <code>label<\/code> and <code>isRequired<\/code> from each form field. <\/p>\n\n\n\n<p> Our query retrieves both <code>inputType<\/code> and <code>type<\/code> values. The sample&#8217;s current component mapping relies on the static type property to determine which Vue component to render. For the scope of this article, the <code>inputType<\/code> is still included in the query output to point out the new support.   <\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"input-type-prop\"><code>inputType<\/code> Prop<\/h3>\n\n\n\n<p>For other use cases outside the scope of this article, you can leverage <a href=\"https:\/\/github.com\/AxeWP\/wp-graphql-gravity-forms\/blob\/5b4f7caf2c8dc590cf621d7a98507dc1fa3c69c8\/docs\/form-field-support.md?plain=1#L110\">the <code>inputType<\/code> property <\/a>instead of the static <code>type<\/code> to dynamically determine which component to render for each Gravity Forms field.  <\/p>\n\n\n\n<p>This dynamic approach allows a single form field to resolve into multiple input types\u2014such as a Quiz Field that can be rendered as either a Checkbox or Radio Field\u2014based on its configuration. Using the <code>inputType<\/code> allows your code to automatically map to the correct component, making it a bit more flexible and easier to maintain as new input variants are introduced.  Stay tuned for a future article that focuses on this!<\/p>\n\n\n\n<p>Check out the <a href=\"https:\/\/github.com\/AxeWP\/wp-graphql-gravity-forms#readme\">WPGraphQL for Gravity Forms readme<\/a> for more documentation on <code>gfForm<\/code> , the <code>FormField<\/code> interface and other queries and mutations the plugin offers.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"querying-for-the-form-in-nuxt\">Querying for the form in Nuxt<\/h2>\n\n\n\n<p>Now that we know what a query for a form looks like and the <code>FormField<\/code> interface the types inherit let\u2019s see how we can use it in our Nuxt app.<\/p>\n\n\n\n<p>Open up the Nuxt app in your code editor and navigate to the <code>composables\/useGravityForm.js<\/code> file.&nbsp;&nbsp;<\/p>\n\n\n\n<p>This file is a Nuxt.js composable designed to interface with WPGraphQL for fetching Gravity Forms data. It imports the <a href=\"https:\/\/vuejs.org\/guide\/essentials\/template-refs\">ref function<\/a> from Vue and the runtime configuration <a href=\"https:\/\/nuxt.com\/docs\/api\/composables\/use-runtime-config\">using useRuntimeConfig<\/a> from Nuxt\u2019s <code>#app<\/code> alias. It defines a reactive variable called <code>formFields<\/code> that will hold the array of form field objects retrieved from the backend.<\/p>\n\n\n\n<p>A multi-line GraphQL query named <code>formQuery <\/code>is declared to fetch a Gravity Form\u2019s fields by its ID. The query leverages GraphQL interfaces to abstract common properties shared by multiple field types. <\/p>\n\n\n\n<p>For more complex field configurations, inline fragments on <code>GfFieldWithChoicesSetting<\/code> fetch choices and input details, while <code>GfFieldWithConditionalLogicSetting<\/code> retrieves any conditional logic rules defined on the field:<br><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> formQuery = `\n<\/span><\/span><span class='shcb-loc'><span>  query GetGravityForm($formId: ID!) {\n<\/span><\/span><span class='shcb-loc'><span>  gfForm(id: $formId, idType: DATABASE_ID) {\n<\/span><\/span><span class='shcb-loc'><span>    formFields(first: <span class=\"hljs-number\">300<\/span>) {\n<\/span><\/span><span class='shcb-loc'><span>      nodes {\n<\/span><\/span><span class='shcb-loc'><span>        id\n<\/span><\/span><span class='shcb-loc'><span>        databaseId\n<\/span><\/span><span class='shcb-loc'><span>        inputType\n<\/span><\/span><span class='shcb-loc'><span>        type\n<\/span><\/span><span class='shcb-loc'><span>        visibility\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithLabelSetting {\n<\/span><\/span><span class='shcb-loc'><span>          label\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithRulesSetting {\n<\/span><\/span><span class='shcb-loc'><span>          isRequired\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithCssClassSetting {\n<\/span><\/span><span class='shcb-loc'><span>          cssClass\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithDefaultValueSetting {\n<\/span><\/span><span class='shcb-loc'><span>          defaultValue\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithSizeSetting {\n<\/span><\/span><span class='shcb-loc'><span>          size\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithPlaceholderSetting {\n<\/span><\/span><span class='shcb-loc'><span>          placeholder\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithMaxLengthSetting {\n<\/span><\/span><span class='shcb-loc'><span>          maxLength\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithInputMaskSetting {\n<\/span><\/span><span class='shcb-loc'><span>          inputMaskValue\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithChoicesSetting {\n<\/span><\/span><span class='shcb-loc'><span>          choices {\n<\/span><\/span><span class='shcb-loc'><span>            text\n<\/span><\/span><span class='shcb-loc'><span>            value\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          inputs {\n<\/span><\/span><span class='shcb-loc'><span>            id\n<\/span><\/span><span class='shcb-loc'><span>            label\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>        ... on GfFieldWithConditionalLogicSetting {\n<\/span><\/span><span class='shcb-loc'><span>          conditionalLogic {\n<\/span><\/span><span class='shcb-loc'><span>            actionType\n<\/span><\/span><span class='shcb-loc'><span>            logicType\n<\/span><\/span><span class='shcb-loc'><span>            rules {\n<\/span><\/span><span class='shcb-loc'><span>              fieldId\n<\/span><\/span><span class='shcb-loc'><span>              operator\n<\/span><\/span><span class='shcb-loc'><span>              value\n<\/span><\/span><span class='shcb-loc'><span>            }\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>      }\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>The <code>fetchForm<\/code> function is defined to send a POST request to the WordPress GraphQL endpoint using Nuxt&#8217;s <code>useFetch<\/code> <a href=\"https:\/\/nuxt.com\/docs\/api\/composables\/use-fetch\">composable<\/a>. It includes a request body that contains the query and variables, with a default <code>formId <\/code>of <code>\"1\"<\/code> to retrieve a specific form.\u00a0 In this line containing the body object, go ahead and replace the integer with your specific ID:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span> body: <span class=\"hljs-built_in\">JSON<\/span>.stringify({\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">query<\/span>: formQuery,\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">variables<\/span>: { <span class=\"hljs-attr\">formId<\/span>: <span class=\"hljs-string\">\"1\"<\/span> }, <span class=\"hljs-comment\">\/\/ Default formId (you can change this to what your id is)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        }),\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br>The <code>immediate<\/code> flag shown below is set to <code>false<\/code> so that the fetch operation is not executed automatically, allowing for manual triggering via the execute function.<\/p>\n\n\n\n<p><br>This is important because it provides better control over when data is fetched, optimizing performance and preventing unnecessary network requests. By waiting for a specific point in the component lifecycle\u2014such as when a button is clicked or a user interacts with the page\u2014we ensure that data is only fetched when needed. In this case, we trigger the fetch manually within the <a href=\"https:\/\/vuejs.org\/api\/composition-api-lifecycle#onmounted\">onMountedlifecycle hook<\/a>, which ensures that the data is loaded once the Nuxt page component rendering the form is attached to the DOM:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>immediate: <span class=\"hljs-literal\">false<\/span>, <span class=\"hljs-comment\">\/\/ Prevent automatic execution<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">transform<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">res<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (res.errors) {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"GraphQL Errors:\"<\/span>, res.errors);\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(res.errors&#91;<span class=\"hljs-number\">0<\/span>].message);\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">const<\/span> fields = res.data?.gfForm?.formFields?.nodes;\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (!<span class=\"hljs-built_in\">Array<\/span>.isArray(fields)) {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Invalid fields data:\"<\/span>, res.data);\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">\"Invalid form fields data\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">return<\/span> fields;\n<\/span><\/span><span class='shcb-loc'><span>        },\n<\/span><\/span><span class='shcb-loc'><span>      }\n<\/span><\/span><span class='shcb-loc'><span>    );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ Return execute to manually trigger the fetch later<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> { data, status, fetchError, execute, refresh };\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"submitting-the-form-in-nuxt\">Submitting the form in Nuxt<\/h3>\n\n\n\n<p>Staying in the <code>useGravityForm.js<\/code> file, we finish off the logic to allow the user to submit form data to our WordPress backend via Nuxt.&nbsp;&nbsp;<\/p>\n\n\n\n<p>We do this with the <code>submitForm<\/code> function.&nbsp; This is an asynchronous function that accepts a form ID and field values, transforms these values using <code>transformFieldValue<\/code>, and then submits them via a GraphQL mutation.&nbsp;<\/p>\n\n\n\n<p>This mutation sends the form ID and the transformed field values to the backend, which responds with either errors or confirmation details. Finally, the composable returns an object containing <code>formFields<\/code>,<code> fetchForm<\/code>, and <code>submitForm<\/code> so that other parts of the Nuxt application can fetch and submit Gravity Forms data:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> submitForm = <span class=\"hljs-keyword\">async<\/span> (formId, fieldValues) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">try<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> transformedValues = <span class=\"hljs-built_in\">Object<\/span>.entries(fieldValues)\n<\/span><\/span><span class='shcb-loc'><span>        .map(<span class=\"hljs-function\">(<span class=\"hljs-params\">&#91;id, value]<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">const<\/span> field = formFields.value.find(\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-function\">(<span class=\"hljs-params\">f<\/span>) =&gt;<\/span> f.databaseId === <span class=\"hljs-built_in\">parseInt<\/span>(id, <span class=\"hljs-number\">10<\/span>)\n<\/span><\/span><span class='shcb-loc'><span>          );\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">if<\/span> (!field) {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-built_in\">console<\/span>.warn(<span class=\"hljs-string\">`No field found for ID <span class=\"hljs-subst\">${id}<\/span>`<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>          }\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">return<\/span> transformFieldValue(field, value);\n<\/span><\/span><span class='shcb-loc'><span>        })\n<\/span><\/span><span class='shcb-loc'><span>        .filter(<span class=\"hljs-built_in\">Boolean<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> mutation = <span class=\"hljs-string\">`<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        mutation SubmitForm($formId: ID!, $fieldValues: &#91;FormFieldValuesInput!]!) {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">          submitGfForm(input: {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            id: $formId<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            fieldValues: $fieldValues<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">          }) {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            errors {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              id<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              message<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            confirmation {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              message<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              type<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            entry {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              id<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              ... on GfSubmittedEntry {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">                databaseId<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">              }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">            }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">          }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      `<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(config.public.wordpressUrl, {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">headers<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">Accept<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        },\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify({\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">query<\/span>: mutation,\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-attr\">variables<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-attr\">formId<\/span>: <span class=\"hljs-built_in\">parseInt<\/span>(formId, <span class=\"hljs-number\">10<\/span>),\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-attr\">fieldValues<\/span>: transformedValues,\n<\/span><\/span><span class='shcb-loc'><span>          },\n<\/span><\/span><span class='shcb-loc'><span>        }),\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> response.json();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">if<\/span> (result.errors) {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(result.errors.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">e<\/span>) =&gt;<\/span> e.message).join(<span class=\"hljs-string\">\", \"<\/span>));\n<\/span><\/span><span class='shcb-loc'><span>      }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">return<\/span> result.data.submitGfForm;\n<\/span><\/span><span class='shcb-loc'><span>    } <span class=\"hljs-keyword\">catch<\/span> (error) {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Submit form error:\"<\/span>, error);\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">throw<\/span> error;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> { formFields, fetchForm, submitForm };\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"rendering-the-form\">Rendering The Form<\/h2>\n\n\n\n<p>Now that we know how the form data is being queried for and submitted, let\u2019s check out where this logic is being used, how the state is being managed, and where the data is being rendered.<br><br>Navigate over to <code>pages\/headlesswp-gform\/index.vue<\/code>.&nbsp;&nbsp;<\/p>\n\n\n\n<p>Take a look at the entire file in your code editor. Let\u2019s break it down from top to bottom.<\/p>\n\n\n\n<p>The script starts by importing Vue\u2019s reactive functions (<code>ref, reactive<\/code>, <code>onMounted, watch<\/code>) and several form field components (e.g., <code>InputField, EmailField<\/code> &#8211;<em>Don\u2019t worry, we will discuss where these are coming from in the next section<\/em>) to render the form.<\/p>\n\n\n\n<p>It then imports the <code>useGravityForm<\/code> composable, which provides functions to fetch form metadata and submit form data from WPGraphQL:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { ref, reactive, onMounted, watch } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"vue\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  InputField,\n<\/span><\/span><span class='shcb-loc'><span>  DropdownField,\n<\/span><\/span><span class='shcb-loc'><span>  ChoiceListField,\n<\/span><\/span><span class='shcb-loc'><span>  AddressField,\n<\/span><\/span><span class='shcb-loc'><span>  DateField,\n<\/span><\/span><span class='shcb-loc'><span>  TimeField,\n<\/span><\/span><span class='shcb-loc'><span>  NameField,\n<\/span><\/span><span class='shcb-loc'><span>  PhoneField,\n<\/span><\/span><span class='shcb-loc'><span>} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"~\/components\/form-fields\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> EmailFieldComponent <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"~\/components\/form-fields\/EmailField.vue\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> useGravityForm <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"~\/composables\/useGravityForm\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> { fetchForm, submitForm, formFields } = useGravityForm();\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Following that, a reactive reference <code>formValues<\/code> is declared using <code>ref({}) <\/code>to store user input for each form field.<\/p>\n\n\n\n<p>Then we establish a reactive error storage object for both address and email validations. It defines a <code>validateAddress<\/code> function that checks if each required component of an address is present and formatted correctly, updating error messages as needed.&nbsp;<\/p>\n\n\n\n<p>Similarly, the <code>validateEmail<\/code> function uses a regular expression to confirm that the email address adheres to a valid format. If any validation fails, the corresponding error message is set and the function returns false. This client-side validation ensures that only complete and correctly formatted data is submitted, improving user experience and data integrity.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> formValues = ref({});\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> error = ref(<span class=\"hljs-literal\">null<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> validationErrors = reactive({\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">address<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">street<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">city<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">state<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">zip<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">country<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  },\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">email<\/span>: <span class=\"hljs-literal\">null<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ Validate the entire address object and update errors per field.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> validateAddress = <span class=\"hljs-function\">(<span class=\"hljs-params\">address<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">let<\/span> valid = <span class=\"hljs-literal\">true<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!address.street) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.street = <span class=\"hljs-string\">\"Street address is required.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    valid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">else<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.street = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!address.city) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.city = <span class=\"hljs-string\">\"City is required.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    valid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">else<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.city = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!address.state) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.state = <span class=\"hljs-string\">\"State is required.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    valid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">else<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.state = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!address.zip || !<span class=\"hljs-regexp\">\/^\\d{5}$\/<\/span>.test(address.zip)) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.zip = <span class=\"hljs-string\">\"Please enter a valid 5-digit ZIP code.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    valid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">else<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.zip = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!address.country) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.country = <span class=\"hljs-string\">\"Country is required.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    valid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  } <span class=\"hljs-keyword\">else<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.address.country = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> valid;\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ Validate the email value and update the error.<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> validateEmail = <span class=\"hljs-function\">(<span class=\"hljs-params\">email<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> emailRegex = <span class=\"hljs-regexp\">\/^&#91;a-zA-Z0-9._%+-]+@&#91;a-zA-Z0-9.-]+\\.&#91;a-zA-Z]{2,}$\/<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (!emailRegex.test(email)) {\n<\/span><\/span><span class='shcb-loc'><span>    validationErrors.email = <span class=\"hljs-string\">\"Please enter a valid email address.\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  validationErrors.email = <span class=\"hljs-literal\">null<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Next, The <code>updateFieldValue<\/code> function merges the current <code>formValues<\/code> with a new value for a given field ID, ensuring that changes to input fields update the reactive state.<br><\/p>\n\n\n\n<p>Inside the <code>onMounted<\/code> lifecycle hook, the code calls <code>fetchForm()<\/code> to get the form metadata and immediately triggers the fetch using <code>execute()<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ Handle field value updates<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> updateFieldValue = <span class=\"hljs-function\">(<span class=\"hljs-params\">fieldId, value<\/span>) =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  formValues.value = {\n<\/span><\/span><span class='shcb-loc'><span>    ...formValues.value,\n<\/span><\/span><span class='shcb-loc'><span>    &#91;fieldId]: value,\n<\/span><\/span><span class='shcb-loc'><span>  };\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>onMounted(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { data, <span class=\"hljs-attr\">error<\/span>: fetchError, execute } = fetchForm();\n<\/span><\/span><span class='shcb-loc'><span>  execute();\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><a href=\"https:\/\/vuejs.org\/guide\/essentials\/watchers.html#watchers\">A watcher<\/a> on the returned data initializes <code>formFields<\/code> and builds an <code>initialValues<\/code> object based on the field type, setting default values (e.g., an object for addresses, and an empty array for checkboxes).<br><\/p>\n\n\n\n<p>For example, if a field is of type &#8220;ADDRESS,&#8221; the code sets its default value to an object with empty strings for<code> street, lineTwo, city, state, zip,<\/code> and a default<code> country<\/code> of &#8220;US.&#8221;<br><\/p>\n\n\n\n<p>A separate watcher monitors <code>fetchError<\/code> and updates the local error reference with the error message if the fetch fails:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>watch(data, (newData) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (newData &amp;&amp; <span class=\"hljs-keyword\">Array<\/span>.isArray(newData)) {\n<\/span><\/span><span class='shcb-loc'><span>      formFields.value = newData;\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">const<\/span> initialValues = {};\n<\/span><\/span><span class='shcb-loc'><span>      newData.<span class=\"hljs-keyword\">forEach<\/span>((field) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-keyword\">switch<\/span> (field.type) {\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"ADDRESS\"<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>            initialValues&#91;field.databaseId] = {\n<\/span><\/span><span class='shcb-loc'><span>              street: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              lineTwo: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              city: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              state: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              zip: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              country: <span class=\"hljs-string\">\"US\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>            };\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">break<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"CHECKBOX\"<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"MULTISELECT\"<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>            initialValues&#91;field.databaseId] = &#91;];\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">break<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"NAME\"<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>            initialValues&#91;field.databaseId] = {\n<\/span><\/span><span class='shcb-loc'><span>              prefix: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              first: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              middle: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              last: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>              suffix: <span class=\"hljs-string\">\"\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>            };\n<\/span><\/span><span class='shcb-loc'><span>            <span class=\"hljs-keyword\">break<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>          <span class=\"hljs-keyword\">default<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>            initialValues&#91;field.databaseId] = <span class=\"hljs-string\">\"\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      formValues.value = initialValues;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  watch(fetchError, (err) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">if<\/span> (err) {\n<\/span><\/span><span class='shcb-loc'><span>      error.value = err.message;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  });\n<\/span><\/span><span class='shcb-loc'><span>});\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The <code>handleSubmit<\/code> function validates the email and address fields by checking their corresponding values in <code>formValues<\/code> and displays an alert if validation fails:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> handleSubmit = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> isValid = <span class=\"hljs-literal\">true<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Validate email field before submission<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> emailField = formFields.value.find(<span class=\"hljs-function\">(<span class=\"hljs-params\">field<\/span>) =&gt;<\/span> field.type === <span class=\"hljs-string\">\"EMAIL\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (emailField &amp;&amp; formValues.value&#91;emailField.databaseId]) {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!validateEmail(formValues.value&#91;emailField.databaseId])) {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0isValid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Validate address field before submission<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> addressField = formFields.value.find(\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-function\">(<span class=\"hljs-params\">field<\/span>) =&gt;<\/span> field.type === <span class=\"hljs-string\">\"ADDRESS\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (addressField &amp;&amp; formValues.value&#91;addressField.databaseId]) {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!validateAddress(formValues.value&#91;addressField.databaseId])) {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0isValid = <span class=\"hljs-literal\">false<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!isValid) {\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0alert(<span class=\"hljs-string\">\"Please fix the errors before submitting.\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>If validation passes, it calls <code>submitForm<\/code> with the current form values, transforming them as needed and handling the response for errors or confirmation.<br><\/p>\n\n\n\n<p>On successful submission, the form is reset by building a new object (<code>resetValues<\/code>) with default values for each field, which is then assigned to <code>formValues.value<\/code>.<br><\/p>\n\n\n\n<p>Finally, the template loops over <code>formFields<\/code> and dynamically renders the appropriate component for each field type (using the<code> fieldComponents<\/code> <code>mapping<\/code>), binding each component\u2019s value to <code>formValues<\/code> via <code>v-model<\/code> and providing a submit button to send the form data:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>try {\n<\/span><\/span><span class='shcb-loc'><span>    const response = await submitForm(1, formValues.value);\n<\/span><\/span><span class='shcb-loc'><span>    if (response?.errors?.length &gt; 0) {\n<\/span><\/span><span class='shcb-loc'><span>      throw new Error(response.errors&#91;0].message);\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>    if (response?.confirmation) {\n<\/span><\/span><span class='shcb-loc'><span>      const temp = document.createElement(\"div\");\n<\/span><\/span><span class='shcb-loc'><span>      temp.innerHTML = response.confirmation.message;\n<\/span><\/span><span class='shcb-loc'><span>      const cleanMessage = temp.textContent || temp.innerText;\n<\/span><\/span><span class='shcb-loc'><span>      alert(cleanMessage);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      \/\/ Reset fields after submission\n<\/span><\/span><span class='shcb-loc'><span>      const resetValues = {};\n<\/span><\/span><span class='shcb-loc'><span>      formFields.value.forEach((field) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>        switch (field.type) {\n<\/span><\/span><span class='shcb-loc'><span>          case \"ADDRESS\":\n<\/span><\/span><span class='shcb-loc'><span>            resetValues&#91;field.databaseId] = {\n<\/span><\/span><span class='shcb-loc'><span>              street: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              lineTwo: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              city: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              state: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              zip: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              country: \"US\",\n<\/span><\/span><span class='shcb-loc'><span>            };\n<\/span><\/span><span class='shcb-loc'><span>            break;\n<\/span><\/span><span class='shcb-loc'><span>          case \"CHECKBOX\":\n<\/span><\/span><span class='shcb-loc'><span>          case \"MULTISELECT\":\n<\/span><\/span><span class='shcb-loc'><span>            resetValues&#91;field.databaseId] = &#91;];\n<\/span><\/span><span class='shcb-loc'><span>            break;\n<\/span><\/span><span class='shcb-loc'><span>          case \"NAME\":\n<\/span><\/span><span class='shcb-loc'><span>            resetValues&#91;field.databaseId] = {\n<\/span><\/span><span class='shcb-loc'><span>              prefix: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              first: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              middle: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              last: \"\",\n<\/span><\/span><span class='shcb-loc'><span>              suffix: \"\",\n<\/span><\/span><span class='shcb-loc'><span>            };\n<\/span><\/span><span class='shcb-loc'><span>            break;\n<\/span><\/span><span class='shcb-loc'><span>          default:\n<\/span><\/span><span class='shcb-loc'><span>            resetValues&#91;field.databaseId] = \"\";\n<\/span><\/span><span class='shcb-loc'><span>        }\n<\/span><\/span><span class='shcb-loc'><span>      });\n<\/span><\/span><span class='shcb-loc'><span>      formValues.value = resetValues;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  } catch (err) {\n<\/span><\/span><span class='shcb-loc'><span>    alert(`Error submitting form: ${err.message}`);\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\/\/ Map field types to components\n<\/span><\/span><span class='shcb-loc'><span>const fieldComponents = {\n<\/span><\/span><span class='shcb-loc'><span>  TEXT: TextField,\n<\/span><\/span><span class='shcb-loc'><span>  EMAIL: EmailField,\n<\/span><\/span><span class='shcb-loc'><span>  TEXTAREA: TextAreaField,\n<\/span><\/span><span class='shcb-loc'><span>  SELECT: SelectField,\n<\/span><\/span><span class='shcb-loc'><span>  MULTISELECT: MultiSelectField,\n<\/span><\/span><span class='shcb-loc'><span>  ADDRESS: AddressField,\n<\/span><\/span><span class='shcb-loc'><span>  CHECKBOX: CheckboxField,\n<\/span><\/span><span class='shcb-loc'><span>  DATE: DateField,\n<\/span><\/span><span class='shcb-loc'><span>  TIME: TimeField,\n<\/span><\/span><span class='shcb-loc'><span>  NAME: NameField,\n<\/span><\/span><span class='shcb-loc'><span>  WEBSITE: WebsiteField,\n<\/span><\/span><span class='shcb-loc'><span>};\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">script<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"p-4\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">&lt;!-- Global error message --&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">v-if<\/span>=<span class=\"hljs-string\">\"error\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"text-red-600\"<\/span>&gt;<\/span>Error: {{ error }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">v-else-if<\/span>=<span class=\"hljs-string\">\"!formFields.length\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Loading form...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">v-else<\/span> @<span class=\"hljs-attr\">submit.prevent<\/span>=<span class=\"hljs-string\">\"handleSubmit\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">v-for<\/span>=<span class=\"hljs-string\">\"field in formFields\"<\/span> <span class=\"hljs-attr\">:key<\/span>=<span class=\"hljs-string\">\"field.databaseId\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"mb-4\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">component<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:is<\/span>=<span class=\"hljs-string\">\"fieldComponents&#91;field.type]\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:field<\/span>=<span class=\"hljs-string\">\"field\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:model-value<\/span>=<span class=\"hljs-string\">\"formValues&#91;field.databaseId]\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          @<span class=\"hljs-attr\">update:model-value<\/span>=<span class=\"hljs-string\">\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">            (newValue) =&gt; updateFieldValue(field.databaseId, newValue)<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\"><span class=\"hljs-string\">          \"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:validation-errors<\/span>=<span class=\"hljs-string\">\"validationErrors\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:validate-email<\/span>=<span class=\"hljs-string\">\"validateEmail\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">          <span class=\"hljs-attr\">:validate-address<\/span>=<span class=\"hljs-string\">\"validateAddress\"<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">        \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"bg-blue-500 text-white px-4 py-2 rounded\"<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>        Submit\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"field-component-rendering\">Field Component Rendering<\/h2>\n\n\n\n<p>Now let\u2019s discuss where those field component imports were coming from in the previous section.&nbsp; Navigate to <code>components\/field-forms<\/code>.&nbsp; &nbsp; This folder contains all the component files for the fields. &nbsp; <\/p>\n\n\n\n<p>In our project, we organized the form field components into groups based on shared behavior and UI patterns. We consolidated similar text-based inputs\u2014like Text, Email, Website, and even Text Area\u2014into a single <code>InputField <\/code>component. <\/p>\n\n\n\n<p>For fields that use dropdowns, we combined Select and MultiSelect into a unified <code>DropdownField<\/code> component. For fields that involve multiple choice inputs, such as Checkbox and Radio fields, we created a consolidated <code>ChoiceListField<\/code> component. <\/p>\n\n\n\n<p>Meanwhile, fields with unique layouts or behaviors (like<code> AddressField<\/code>, <code>DateField<\/code>, <code>TimeField<\/code>, and <code>NameField<\/code>) were kept as separate components. <\/p>\n\n\n\n<p>To simplify importing these components into our main form, we created a barrel file (<code>index.js<\/code>) in the <code>form-fields<\/code> folder that re-exports all of them. <\/p>\n\n\n\n<p>Since there are a few,&nbsp;let\u2019s just break down the common patterns they follow:<br><\/p>\n\n\n\n<p>&nbsp;&nbsp;<strong>1. Props Definition<\/strong><\/p>\n\n\n\n<p>All components define a consistent set of props:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>field<\/code>: An object containing field metadata (required)<\/li>\n<\/ul>\n\n\n\n<p>Contains information like <code>databaseId<\/code>, label, <code>isRequired<\/code>, and field-specific properties<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>modelValue<\/code>: The current value of the field<\/li>\n<\/ul>\n\n\n\n<p>Type varies based on the field (string, array, object) <\/p>\n\n\n\n<p>Includes appropriate default values<\/p>\n\n\n\n<p><strong>2. Event Handling<\/strong><\/p>\n\n\n\n<p>Each component emits events to update the parent component&#8217;s state:<\/p>\n\n\n\n<p>All components use the <code>update:modelValue<\/code> or <code>update:model-value<\/code> event for two-way binding.<\/p>\n\n\n\n<p>This follows Vue&#8217;s convention for custom v-model implementation<\/p>\n\n\n\n<p><strong>3. Field-Specific Validation<\/strong><\/p>\n\n\n\n<p>Many components include field-specific validation logic:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Simple fields may validate on input<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Complex fields (like <code>EmailField<\/code>, <code>AddressField<\/code>) have dedicated validation functions<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Error messages are stored in reactive variables and displayed in the template<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><strong>4. Consistent Template Structure<\/strong><\/p>\n\n\n\n<p>All components follow a similar template structure:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A wrapper div with class field-wrapper<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A label displaying the field name and required indicator if needed<\/li>\n<\/ul>\n\n\n\n<p>Input element(s) with appropriate bindings:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>:value<\/code> bound to the model value<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Event handlers to emit update events<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Error message displayed when validation fails<\/li>\n<\/ul>\n\n\n\n<p>\u200b\u200b<\/p>\n\n\n\n<p><strong>5. Complex Field Handling<\/strong><\/p>\n\n\n\n<p>For complex fields (like Address, Name):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Data is structured as objects with multiple properties<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Components use appropriate layout techniques (grid, flexbox) to organize multiple inputs<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Updates maintain the overall object structure while changing specific properties<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"error-handling\">What\u2019s the deal with Errors and Why do we handle them?<\/h2>\n\n\n\n<p>What is the deal, Jerry??? Well, the deal is that we handle two types of errors in this app. This would be a great question, the great comedian, Jerry Seinfeld could ask.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Request or Server Errors<\/h3>\n\n\n\n<p>These are errors that prevent the form entry from being saved. In our Nuxt implementation with Gravity Forms, we encounter several types of network-related errors:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Network connectivity issues when the user&#8217;s connection drops<\/li>\n\n\n\n<li>WordPress backend errors (500 Internal Server Error)<\/li>\n\n\n\n<li>Authentication or permission errors when submitting to protected forms<\/li>\n\n\n\n<li>GraphQL syntax or schema errors<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>Our application handles these errors through the try\/catch block in the form submission process. When using the <code>submitForm<\/code> function from our <code>useGravityForm<\/code> composable, we capture server errors and display them prominently to the user with an alert.<br><br>Inside the <code>useGravityForm<\/code> composable, we format GraphQL errors into a user-friendly message that tells the user that the submission failed on a popup in the browser.<\/p>\n\n\n\n<p><br>You can test this error handling by disabling your network connection in DevTools and attempting to submit the form. The application will display an error message indicating the network failure.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"field-validation-errors\">Field Validation Errors<\/h3>\n\n\n\n<p>Our application implements a dual-layer validation approach:<\/p>\n\n\n\n<p>1.<strong>Client-Side Validation<\/strong>: Implemented for specific field types to provide immediate feedback<\/p>\n\n\n\n<p>2.<strong>Server-Side Validation<\/strong>: Handled by the WordPress Gravity Forms backend<\/p>\n\n\n\n<p>When the server returns validation errors, they&#8217;re processed in the <code>handleSubmit<\/code> function of our <code>index.vue<\/code> component. The application checks <code>response?.errors?.length<\/code> to determine if validation errors exist and displays them accordingly.<\/p>\n\n\n\n<p>For client-side validation, certain field types have built-in validation:<\/p>\n\n\n\n<p><strong>Email Field<\/strong>: Validates email format using a regex pattern<\/p>\n\n\n\n<p><strong>Address Field<\/strong>: Validates complete address information and proper postal code formats<\/p>\n\n\n\n<p><strong>Required Fields<\/strong>: All required fields are checked before submission<\/p>\n\n\n\n<p>To test field validation, use the &#8220;Short Strings Only&#8221; field at the bottom of the form. This field is configured in the Gravity Forms admin to accept a maximum of 5 characters. If you enter more than 5 characters and submit the form, the server will reject the submission and return a validation error.<\/p>\n\n\n\n<p>Unlike some fields that implement client-side validation (like email and address), this text field relies on server-side validation in Gravity Forms. The error message will display after the submission attempt, informing you about the 5-character limit constraint.<\/p>\n\n\n\n<p>This demonstrates how our application strategically combines client-side validation for enhanced user experience with server-side validation for critical business rules and data integrity.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"not-included\">What is Not Included<\/h3>\n\n\n\n<p>You can drop these components, and composables and get up and running with Gravity Forms forms quickly in a Nuxt app, but there are features they don\u2019t provide. Some examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Support for Gravity Forms\u2019 Conditional Logic rules<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Rendering an existing Gravity Forms entry and allowing the user to update its field values<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Support for all field types <\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>We hope this blog post helped you understand how to use forms in headless WordPress with Gravity Forms, WPGraphQL for Gravity Forms, and Nuxt!<\/p>\n\n\n\n<p>As always, we\u2019re super stoked to hear your feedback and learn about the headless projects you\u2019re working on, so hit us up in the <a href=\"https:\/\/wpeng.in\/devrel-discord\/\">Headless WordPress Discord<\/a>!<\/p>\n\n\n\n<p>Special thanks to <a href=\"https:\/\/x.com\/itsjustlevine\">David Levine<\/a> and <a href=\"https:\/\/bsky.app\/profile\/danielroe.dev\">Daniel Roe<\/a> for helping me write this article and the code!<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gravity Forms is a WordPress plugin that allows you to create a variety of forms on your WordPress site. Its large selection of add-ons lets you send collected form data [&hellip;]<\/p>\n","protected":false},"author":20,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_EventAllDay":false,"_EventTimezone":"","_EventStartDate":"","_EventEndDate":"","_EventStartDateUTC":"","_EventEndDateUTC":"","_EventShowMap":false,"_EventShowMapLink":false,"_EventURL":"","_EventCost":"","_EventCostDescription":"","_EventCurrencySymbol":"","_EventCurrencyCode":"","_EventCurrencyPosition":"","_EventDateTimeSeparator":"","_EventTimeRangeSeparator":"","_EventOrganizerID":[],"_EventVenueID":[],"_OrganizerEmail":"","_OrganizerPhone":"","_OrganizerWebsite":"","_VenueAddress":"","_VenueCity":"","_VenueCountry":"","_VenueProvince":"","_VenueState":"","_VenueZip":"","_VenuePhone":"","_VenueURL":"","_VenueStateProvince":"","_VenueLat":"","_VenueLng":"","_VenueShowMap":false,"_VenueShowMapLink":false,"footnotes":""},"categories":[23],"tags":[],"class_list":["post-31820","post","type-post","status-publish","format-standard","hentry","category-headless"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.7 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Gravity Forms in Headless WordPress with Nuxt\/Vue - Builders<\/title>\n<meta name=\"description\" content=\"Learn how to integrate Gravity Forms into a headless WordPress setup using Nuxt\/Vue and WPGraphQL. Discover best practices for dynamic form rendering and front-end integration\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Gravity Forms in Headless WordPress with Nuxt\/Vue\" \/>\n<meta property=\"og:description\" content=\"Get stoked on my latest guide on integrating Gravity Forms with Headless WordPress using Nuxt\/Vue and WPGraphQL. Learn insights on setup, dynamic form handling, and best practices for modern web forms.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2025-04-08T15:50:14+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-04-08T15:50:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/04\/WPE-Builders-YouTube-ScreenshotLight-1920x1080-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Francis Agulto\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:site\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Francis Agulto\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/\"},\"author\":{\"name\":\"Francis Agulto\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\"},\"headline\":\"Gravity Forms in Headless WordPress with Nuxt\\\/Vue\",\"datePublished\":\"2025-04-08T15:50:14+00:00\",\"dateModified\":\"2025-04-08T15:50:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/\"},\"wordCount\":2624,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB\",\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/\",\"name\":\"Gravity Forms in Headless WordPress with Nuxt\\\/Vue - Builders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB\",\"datePublished\":\"2025-04-08T15:50:14+00:00\",\"dateModified\":\"2025-04-08T15:50:16+00:00\",\"description\":\"Learn how to integrate Gravity Forms into a headless WordPress setup using Nuxt\\\/Vue and WPGraphQL. Discover best practices for dynamic form rendering and front-end integration\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#primaryimage\",\"url\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB\",\"contentUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-with-nuxt-vue\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Gravity Forms in Headless WordPress with Nuxt\\\/Vue\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"name\":\"Builders\",\"description\":\"Reimagining the way we build with WordPress.\",\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\",\"name\":\"WP Engine\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"contentUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"width\":348,\"height\":68,\"caption\":\"WP Engine\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/wpebuilders\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCh1WuL54XFb9ZI6m6goFv1g\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\",\"name\":\"Francis Agulto\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"caption\":\"Francis Agulto\"},\"description\":\"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/author\\\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Gravity Forms in Headless WordPress with Nuxt\/Vue - Builders","description":"Learn how to integrate Gravity Forms into a headless WordPress setup using Nuxt\/Vue and WPGraphQL. Discover best practices for dynamic form rendering and front-end integration","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/","og_locale":"en_US","og_type":"article","og_title":"Gravity Forms in Headless WordPress with Nuxt\/Vue","og_description":"Get stoked on my latest guide on integrating Gravity Forms with Headless WordPress using Nuxt\/Vue and WPGraphQL. Learn insights on setup, dynamic form handling, and best practices for modern web forms.","og_url":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/","og_site_name":"Builders","article_published_time":"2025-04-08T15:50:14+00:00","article_modified_time":"2025-04-08T15:50:16+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/04\/WPE-Builders-YouTube-ScreenshotLight-1920x1080-1.png","type":"image\/png"}],"author":"Francis Agulto","twitter_card":"summary_large_image","twitter_creator":"@wpebuilders","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Francis Agulto","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/"},"author":{"name":"Francis Agulto","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54"},"headline":"Gravity Forms in Headless WordPress with Nuxt\/Vue","datePublished":"2025-04-08T15:50:14+00:00","dateModified":"2025-04-08T15:50:16+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/"},"wordCount":2624,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"image":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#primaryimage"},"thumbnailUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB","articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/","url":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/","name":"Gravity Forms in Headless WordPress with Nuxt\/Vue - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"primaryImageOfPage":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#primaryimage"},"image":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#primaryimage"},"thumbnailUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB","datePublished":"2025-04-08T15:50:14+00:00","dateModified":"2025-04-08T15:50:16+00:00","description":"Learn how to integrate Gravity Forms into a headless WordPress setup using Nuxt\/Vue and WPGraphQL. Discover best practices for dynamic form rendering and front-end integration","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#primaryimage","url":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB","contentUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXdLQJizEXW_TKhCKvfyaY4ezk4mcUfltTXkNT2TV9K2kxlJzwnmQStAfRjWQ0C7XCkcQKiCFJ15C6fp1xbpfxvmfGSOKIVX4qlPqUsIchzTunlaXvoXV6CGgLCkSc9myPBn4OA23g?key=Vh09bUmp-N0ij2w2pffuOlVB"},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-with-nuxt-vue\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"Gravity Forms in Headless WordPress with Nuxt\/Vue"}]},{"@type":"WebSite","@id":"https:\/\/wpengine.com\/builders\/#website","url":"https:\/\/wpengine.com\/builders\/","name":"Builders","description":"Reimagining the way we build with WordPress.","publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wpengine.com\/builders\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/wpengine.com\/builders\/#organization","name":"WP Engine","url":"https:\/\/wpengine.com\/builders\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/","url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","contentUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","width":348,"height":68,"caption":"WP Engine"},"image":{"@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/wpebuilders","https:\/\/www.youtube.com\/channel\/UCh1WuL54XFb9ZI6m6goFv1g"]},{"@type":"Person","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54","name":"Francis Agulto","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","caption":"Francis Agulto"},"description":"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!","url":"https:\/\/wpengine.com\/builders\/author\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31820","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=31820"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31820\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=31820"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=31820"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=31820"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}