{"id":211,"date":"2021-06-14T13:44:04","date_gmt":"2021-06-14T19:44:04","guid":{"rendered":"https:\/\/developers.wpengine.com\/blog\/?p=211"},"modified":"2026-02-05T21:01:37","modified_gmt":"2026-02-06T03:01:37","slug":"gravity-forms-in-headless-wordpress-gatsby","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/","title":{"rendered":"Gravity Forms in Headless WordPress with Gatsby"},"content":{"rendered":"\n<p>A couple weeks ago, I wrote a <a href=\"https:\/\/wpengine.com\/builders\/work-with-gravity-forms-in-headless-wordpress\/\">Gravity Forms in Headless WordPress<\/a> blog post, and recorded a <a href=\"https:\/\/www.youtube.com\/watch?v=WUKo6frHsFw\">YouTube video<\/a> to accompany it. They were both well received, but several members of the headless WordPress community told me that they&#8217;d love to learn how to work with <a href=\"https:\/\/www.gravityforms.com\/\">Gravity Forms<\/a> in a <a href=\"https:\/\/www.gatsbyjs.com\/\">Gatsby<\/a> app, specifically.<\/p>\n\n\n\n<p>In this post, we&#8217;ll learn how we can use <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> on a headless WordPress back-end to send and receive Gravity Forms data to\/from a decoupled front-end Gatsby application. We&#8217;ll see how we can use the unified GraphQL data layer that Gatsby provides to query for our form data at buildtime, then use Apollo Client to submit form entries from the client.<\/p>\n\n\n\n<p>In the <a href=\"https:\/\/github.com\/kellenmace\/gravity-forms-in-headless-wordpress-gatsby\">Gatsby app repo<\/a>, I&#8217;ll provide a suite of React components and a React hook that you can even drop into your Gatsby projects and have Gravity Forms &#8220;just work.&#8221; They&#8217;ll handle rendering out Gravity Forms forms and sending form submissions to your headless WordPress back-end.<\/p>\n\n\n\n<p>Let&#8217;s roll! \ud83c\udfce\ud83d\udca8<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Getting Started<\/h2>\n\n\n\n<p>To benefit from this post, you should be familiar with the basics of working with the command line, local <a href=\"https:\/\/wordpress.org\/\">WordPress<\/a> development, <a href=\"https:\/\/reactjs.org\/\">React<\/a>,  <a href=\"https:\/\/www.gatsbyjs.com\/\">Gatsby<\/a>, and <a href=\"https:\/\/www.apollographql.com\/docs\/react\/\">Apollo Client<\/a>.<\/p>\n\n\n\n<p>Here are the steps for getting set up:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Set up a local WordPress site and get it running<\/li><li>Install and activate the <a href=\"https:\/\/www.gravityforms.com\/\">Gravity Forms<\/a>, <a href=\"https:\/\/wordpress.org\/plugins\/wp-gatsby\/\">WPGatsby<\/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.<\/li><li>Clone down the <a href=\"https:\/\/github.com\/kellenmace\/gravity-forms-in-headless-wordpress-gatsby\">Gatsby app repo<\/a> for this project.<\/li><li>Import the form questionnaire form. From the WordPress admin sidebar, go to <code>Forms<\/code> &gt; <code>Import\/Export<\/code> &gt; <code>Import Forms<\/code>. Select the <code>gravityforms-questionnaire-form.json<\/code> inside Gatsby app&#8217;s folder and click the button to import it.<\/li><li>Create an <code>.env.development<\/code> file inside of the Gatsby app&#8217;s folder. Open that file in a text editor and paste in <code>GATSBY_WORDPRESS_API_URL=http:\/\/wpgraphqlgravtyforms.local\/graphql<\/code>, replacing <code>wpgraphqlgravtyforms.local<\/code> with the domain for your local WordPress site. This is the endpoint that both Gatsby and Apollo Client will use when they send requests to your WordPress back-end.<\/li><li>Run <code>npm install<\/code> or <code>yarn<\/code> to install the dependencies.<\/li><li>Run <code>npm run develop<\/code> to get the server running locally.<\/li><li>You should now be able to visit <a href=\"http:\/\/localhost:8000\/\">http:\/\/localhost:8000\/<\/a> in a web browser and see the app&#8217;s homepage.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">How to Query for a Form<\/h2>\n\n\n\n<p>The <a href=\"https:\/\/www.gatsbyjs.com\/plugins\/gatsby-source-wordpress\/\">gatsby-source-wordpress<\/a> source plugin used by our app takes the GraphQL schema that WPGraphQL and WPGraphQL for Gravity Forms provide and adds it to Gatsby&#8217;s unified GraphQL data layer. So querying for a form looks like this:<\/p>\n\n\n<pre class=\"wp-block-code language-text\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>query getForm {\n<\/span><\/span><span class='shcb-loc'><span>  wpGravityFormsForm(formId: {eq: 4}) {\n<\/span><\/span><span class='shcb-loc'><span>    formId\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 {\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>        type\n<\/span><\/span><span class='shcb-loc'><span>        ... on WpTextField {\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 WpSelectField {\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>You can see that we&#8217;re asking for some data about the form itself; namely, its <code>formId<\/code>, <code>title<\/code> and <code>description<\/code>. Then under <code>formFields<\/code> &gt; <code>nodes<\/code> (which is a <a href=\"https:\/\/www.apollographql.com\/docs\/apollo-server\/schema\/unions-interfaces\/#querying-a-union\">GraphQL interface<\/a>), we use the <code>... on<\/code> syntax to tell GraphQL what data we&#8217;d like back for each type of field. Any field types we include here will have data returned if they exist in the form, and any we have omitted will not\u2013 they&#8217;ll appear as empty objects instead.<\/p>\n\n\n\n<p>We can test out this query right from the GraphiQL IDE that Gatsby provides. Here&#8217;s how:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>With the app running, head over to <a href=\"http:\/\/localhost:8000\/___graphql\">http:\/\/localhost:8000\/___graphql<\/a>.<\/li><li>Paste the query above into the left column, replacing &#8220;4&#8221; in <code>eq: 4<\/code> with the ID of the form you imported.<\/li><li>Click the \u25b6 button to execute the query.<\/li><li>See the results returned in the right column.<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2021\/06\/gravity-forms-gatsby-test-query-1024x708.png\" alt=\"\" class=\"wp-image-212\"\/><\/figure>\n\n\n\n<p>Check out the <a href=\"https:\/\/github.com\/harness-software\/wp-graphql-gravity-forms#readme\">WPGraphQL for Gravity Forms readme<\/a> for more documentation on this query, as well as other queries and mutations the plugin offers. Just note that many of the fields will have different names and input fields (such as <code>wpGravityFormsForm<\/code> instead of <code>gravityFormsForm<\/code>). That&#8217;s because Gatsby modifies the WPGraphQL schema a bit before adding it to its unified GraphQL data layer schema.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Query for a Form in our App<\/h2>\n\n\n\n<p>Now that we know what a query for a form looks like let&#8217;s see how we can use it in our Gatsby app.<\/p>\n\n\n\n<p>Open up <code>gatsby-node.js<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code language-js\" aria-describedby=\"shcb-language-1\" 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> path = <span class=\"hljs-built_in\">require<\/span>(<span class=\"hljs-string\">\"path\"<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>exports.createPages = <span class=\"hljs-keyword\">async<\/span> ({ actions }) =&gt; {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { createPage } = actions;\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> questionnaireTemplate = path.resolve(<span class=\"hljs-string\">`src\/templates\/questionnaire.tsx`<\/span>);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ Create Questionnaire page, and pass the Gravity Forms form ID to it.<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  createPage({\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">path<\/span>: <span class=\"hljs-string\">`\/questionnaire`<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">component<\/span>: questionnaireTemplate,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">context<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">formId<\/span>: <span class=\"hljs-number\">4<\/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-1\"><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>Notice that we call the <code>createPage()<\/code> function to tell Gatsby to create a page at <code>\/questionnaire<\/code> using the <code>src\/templates\/questionnaire.tsx<\/code> template, and that we pass in a <code>formId<\/code> of <code>4<\/code> for the component&#8217;s context.<\/p>\n\n\n\n<p>Go ahead and swap out this form ID with the ID of the form you imported.<\/p>\n\n\n\n<p>Now try restarting the local dev server, then navigating to <a href=\"http:\/\/localhost:8000\/questionnaire\">http:\/\/localhost:8000\/questionnaire<\/a>. You should see the form&#8217;s title, description and fields displayed in all their barely-even-styled-at-all glory! ????<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2021\/05\/headless-wordpress-gravity-form-1024x845.png\" alt=\"Screenshot of a rendered Gravity Forms form\" class=\"wp-image-177\"\/><\/figure>\n\n\n\n<p>But where&#8217;s all this form data-fetching magic happening? Let&#8217;s dig a bit deeper. Open up the <code>src\/templates\/questionnaire.tsx<\/code>. page template.<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" aria-describedby=\"shcb-language-2\" 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\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Questionnaire<\/span>(<span class=\"hljs-params\">{ data }: Props<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { <span class=\"hljs-attr\">wpGravityFormsForm<\/span>: form } = data;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{form.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{form.description}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GravityForm<\/span> <span class=\"hljs-attr\">form<\/span>=<span class=\"hljs-string\">{form}<\/span> \/&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">  );<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">}<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> query = graphql<span class=\"hljs-string\">`<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">  query getForm($formId: Int!) {<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">    wpGravityFormsForm(formId: { eq: $formId }) {<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">      ...GravityFormFields<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">    }<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">  }<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-string\">`<\/span>;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/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\">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>You&#8217;ll see that we&#8217;re exporting a <code>graphql<\/code> tagged template literal containing our <code>getForm<\/code> GraphQL query. It accepts <code>formId<\/code> as an argument, then fetches the form with that ID.<\/p>\n\n\n\n<p>The <code>...GravityFormFields<\/code> syntax makes use of this GraphQL fragment defined in <code>src\/components\/GravityFormsForm.tsx<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" 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><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> GRAVITY_FORM_FIELDS = graphql<span class=\"hljs-string\">`<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  fragment GravityFormFields on WpGravityFormsForm {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    formId<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    title<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    description<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    button {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      text<\/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\">    confirmations {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      isDefault<\/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\">    formFields {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      nodes {<\/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\">        type<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        ...AddressFieldFields<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        ...CheckboxFieldFields<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        # etc.<\/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><\/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>It specifies what data we want to fetch for the form. Inside, it leverages even more fragments\u2013 one for each type of field. You can see those individual field fragments in each file inside of the <code>src\/components\/GravityFormsFields<\/code> folder.<\/p>\n\n\n\n<p>These GraphQL fields <em>could<\/em> have all been hardcoded in the <code>src\/templates\/questionnaire.tsx<\/code> file, but using GraphQL fragments allows us to co-locate each component&#8217;s data requirements with that component itself. That way, things are more organized.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Render the Form<\/h2>\n\n\n\n<p>Now that we know how the form data is being queried and passed into the <code>Questionnaire<\/code> page component, let&#8217;s see how we can use that data to render our form.<\/p>\n\n\n\n<p>Head back over to <code>src\/templates\/questionnaire.tsx<\/code>. You&#8217;ll see that the form&#8217;s <code>title<\/code> and <code>description<\/code> render inside of <code>h1<\/code> and <code>p<\/code> tags, respectively, and then a <code>&lt;GravityForm \/&gt;<\/code> component is rendered, with the <code>form<\/code> passed to it as a prop:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" aria-describedby=\"shcb-language-4\" 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><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>{form.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>{form.description}<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\">GravityForm<\/span> <span class=\"hljs-attr\">form<\/span>=<span class=\"hljs-string\">{form}<\/span> \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/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\">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>Open <code>src\/components\/GravityForm.tsx<\/code>, where the <code>GravityForm<\/code> component is defined, and you&#8217;ll see that it renders a <code>GravityFormsForm<\/code> component, which is wrapped in a <code>GravityFormProvider<\/code> context provider:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" aria-describedby=\"shcb-language-5\" 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><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GravityFormProvider<\/span>&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GravityFormsForm<\/span> {<span class=\"hljs-attr\">...props<\/span>} \/&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">GravityFormProvider<\/span>&gt;<\/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\">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>Don&#8217;t worry about <code>GravityFormProvider<\/code> just yet\u2013 we&#8217;ll learn about that in the next section. For now, let&#8217;s focus on the <code>GravityFormsForm<\/code> component rendered inside of it. Navigate to <code>src\/components\/GravityFormsForm.tsx<\/code> next.<\/p>\n\n\n\n<p><code>GravityFormsForm<\/code> accepts <code>form<\/code> as a prop and renders an HTML <code>&lt;form&gt;<\/code> element with the form fields, any error messages, and the submit button inside. You&#8217;ll notice that it also contains a <code>handleSubmit<\/code> function to serve as the <code>onSubmit<\/code> event handler for our form. When the form is submitted, the <code>submitForm<\/code> mutation (defined at the top of the file) executes with <code>formId<\/code> and <code>fieldValues<\/code> passed in as variables.<\/p>\n\n\n\n<p>Next, let&#8217;s take a look at how our fields are rendering. Inside of the <code>&lt;form&gt;<\/code>, we map over the <code>formFields<\/code> and render a <code>GravityFormsField<\/code> component for each field:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" 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>{formFields.map(<span class=\"hljs-function\"><span class=\"hljs-params\">field<\/span> =&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GravityFormsField<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">    <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{field?.id}<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">    <span class=\"hljs-attr\">field<\/span>=<span class=\"hljs-string\">{field<\/span> <span class=\"hljs-attr\">as<\/span> <span class=\"hljs-attr\">FormField<\/span>}<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">    <span class=\"hljs-attr\">fieldErrors<\/span>=<span class=\"hljs-string\">{getFieldErrors(Number(field?.id))}<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">  \/&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span>)}\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>Let&#8217;s keep following this trail and head over to <code>src\/components\/GravityFormsField.tsx<\/code> to check out the <code>GravityFormsField<\/code> component. You&#8217;ll see that it&#8217;s little more than a <code>switch()<\/code> statement:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" 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\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">GravityFormsField<\/span>(<span class=\"hljs-params\">{ field, fieldErrors }: Props<\/span>) <\/span>{\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>      <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">AddressField<\/span> <span class=\"hljs-attr\">field<\/span>=<span class=\"hljs-string\">{field}<\/span> <span class=\"hljs-attr\">fieldErrors<\/span>=<span class=\"hljs-string\">{fieldErrors}<\/span> \/&gt;<\/span><\/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\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CheckboxField<\/span> <span class=\"hljs-attr\">field<\/span>=<span class=\"hljs-string\">{field}<\/span> <span class=\"hljs-attr\">fieldErrors<\/span>=<span class=\"hljs-string\">{fieldErrors}<\/span> \/&gt;<\/span><\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">case<\/span> <span class=\"hljs-string\">\"date\"<\/span>:\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">DateField<\/span> <span class=\"hljs-attr\">field<\/span>=<span class=\"hljs-string\">{field}<\/span> <span class=\"hljs-attr\">fieldErrors<\/span>=<span class=\"hljs-string\">{fieldErrors}<\/span> \/&gt;<\/span><\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-comment\">\/\/ etc.<\/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-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>The appropriate field component is rendered out, depending on the value of <code>field.type. <\/code>When <code>field.type<\/code> encounters an unsupported field, a &#8220;Field type not supported&#8221; message appears instead.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Render the Fields<\/h2>\n\n\n\n<p>From here, you can peruse all of the individual field components inside of the <code>src\/components\/GravityFormsFields<\/code> folder to see how they work. All of them follow the same pattern. They:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Define their data requirements in an exported GraphQL fragment (which get leveraged inside of <code>src\/components\/GravityFormsForm.tsx<\/code>, as mentioned previously)<\/li><li>Define the field component, which accepts these props:<br>&#8211; <code>field<\/code> (the data about this field)<br>&#8211; <code>fieldErrors<\/code> (any errors specific to this field)<br>&#8230;and returns JSX for the field<\/li><li>Use the <code>useGravityForm()<\/code> custom React hook and <br>&#8211; Use its <code>state<\/code> object to get the current value for this field<br>&#8211; Call its <code>dispatch<\/code> function to update the field&#8217;s value in state<br><\/li><\/ol>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>&#8220;Wait, where in blazes did this custom <code>useGravityForm()<\/code> React hook come from?!&#8221;<\/p><cite>\u2013 You, just now<\/cite><\/blockquote>\n\n\n\n<p>I&#8217;m glad you asked! Let&#8217;s find out \u2013<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Managing our Form State<\/h2>\n\n\n\n<p>Remember earlier when we saw that <code>GravityFormProvider<\/code> context provider in <code>src\/components\/GravityForm.tsx<\/code>? That, and the custom <code>useGravityForm()<\/code> hook are defined in <code>src\/hooks\/useGravityForm.tsx<\/code>. Let&#8217;s take a look at that file next to understand better how to manage our form state.<\/p>\n\n\n\n<p>Scroll down to <code>GravityFormProvider<\/code>. This is a React context provider that uses React&#8217;s <code>useReducer()<\/code> and passes down <code>state<\/code> and <code>dispatch<\/code> as its value:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" 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-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">GravityFormProvider<\/span>(<span class=\"hljs-params\">{ children }: { children: ReactNode }<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> &#91;state, dispatch] = useReducer(reducer, DEFAULT_STATE);\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">GravityFormContext.Provider<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">state<\/span>, <span class=\"hljs-attr\">dispatch<\/span> }}&gt;<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">      {children}<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">GravityFormContext.Provider<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">  );<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">}<\/span><\/span><\/span>\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>When <code>dispatch()<\/code> is called from inside of our field components, the <code>reducer()<\/code> function in this file determines how the form state should update. The reducer runs a <code>switch()<\/code> on the type of action that was dispatched and updates the form state accordingly using the <code>action.fieldValue<\/code> data that passed in:<\/p>\n\n\n<pre class=\"wp-block-code language-jsx\" aria-describedby=\"shcb-language-9\" 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\">switch<\/span> (action.type) {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">case<\/span> ACTION_TYPES.updateAddressFieldValue: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> { id, addressValues } = action.fieldValue <span class=\"hljs-keyword\">as<\/span> AddressFieldValue;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> &#91;...getOtherFieldValues(id), { id, addressValues }];\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">case<\/span> ACTION_TYPES.updateCheckboxFieldValue: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">const<\/span> { id, checkboxValues } = action.fieldValue <span class=\"hljs-keyword\">as<\/span> CheckboxFieldValue;\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> &#91;...getOtherFieldValues(id), { id, checkboxValues }];\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/\/ etc.<\/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\">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<h2 class=\"wp-block-heading\">Submitting Forms<\/h2>\n\n\n\n<p>Try submitting a few form entries yourself. After the form submits, it will be removed from the UI and replaced with the &#8220;thank you&#8221; confirmation message for our form.<\/p>\n\n\n\n<p>Back in the WordPress admin, you can navigate to the Gravity Forms Entries page for our form and see your new entries listed. Try clicking on a few of them and confirming that the saved values match what you had entered into the form.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error Handling<\/h2>\n\n\n\n<p>Our Gatsby app handles two types of errors:<\/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. Some examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>The user&#8217;s network connection dropped out<\/li><li>The WordPress back-end threw a 500 Internal Server Error<\/li><li>The request was rejected because only logged-in users with the proper permissions are allowed to submit form entries for that particular form<\/li><\/ul>\n\n\n\n<p>If any of these errors occur, Apollo Client will return an error object. Our app will display errors at the bottom of the form if errors exist, just above the submit button.<\/p>\n\n\n\n<p>To see this error handling in action, you can set your network connection to <code>offline<\/code> in the browser DevTools and then attempt to submit the form while offline.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Field Validation Errors<\/h3>\n\n\n\n<p>Inside the <code>GravityFormsForm<\/code> component, we check <code>data.submitGravityFormsForm.errors<\/code> to see if the server returns any validation errors. If we have field validation errors, we call the <code>getFieldErrors()<\/code> helper function to pull out the errors and pass them through to that particular field that failed validation. The error message text is displayed directly below that field, so the user knows why the field value they entered is invalid.<\/p>\n\n\n\n<p>To test this locally, you can use the &#8220;Short Strings Only&#8221; field at the bottom of the imported form. In the Gravity Forms admin UI, we&#8217;ve configured that field to allow strings with a maximum length of 5 characters. So if you enter more than five characters into it, then try to submit the form, you&#8217;ll see the field validation error text appear below that field.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s Missing?<\/h2>\n\n\n\n<p>This suite of React components and custom hook are a starting point. You can drop them into your project and get up and running with Gravity Forms forms quickly in a Gatsby app, but there are some features they don&#8217;t provide. Some examples:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Client-side validation (beyond the built-in validation the browser performs on simple, single-input fields)<\/li><li>Support for Gravity Forms&#8217; Conditional Logic rules<\/li><li>Rendering an existing Gravity Forms entry and allowing the user to update its field values<\/li><li>Support for all field types (see the section below for more details)<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Which Fields are Supported?<\/h2>\n\n\n\n<p>We&#8217;re using the <code>submitGravityFormsForm<\/code> mutation that WPGraphQL for Gravity Forms provides to keep things simple. This mutation works excellent for allowing us to submit forms with <em>some<\/em> field types, such as inside of our project&#8217;s <code>src\/components\/GravityFormsFields<\/code> folder. However, it does not work for all field types that the WPGraphQL for Gravity Forms plugin supports.<\/p>\n\n\n\n<p>If you want to use other field types, you would instead need to implement a more complex process that involves firing off multiple mutations to build a draft entry incrementally, then finally submit it. This is because currently, the GraphQL specification does not support input union types. You can learn more about that technical limitation by reading&nbsp;<a target=\"_blank\" href=\"https:\/\/github.com\/harness-software\/wp-graphql-gravity-forms\/issues\/4#issuecomment-563305561\" rel=\"noreferrer noopener\">this GitHub issue thread<\/a>&nbsp;and more about the alternative submission process in the&nbsp;<a target=\"_blank\" href=\"https:\/\/github.com\/harness-software\/wp-graphql-gravity-forms#documentation-submit-entry-incrementally\" rel=\"noreferrer noopener\">WPGraphQL for Gravity Forms readme<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Hide Unsupported Fields in the WordPress Admin<\/h2>\n\n\n\n<p>Since not all field types support this approach, you may want to limit the Gravity Forms fields displayed in the WordPress admin to only the supported ones. This way, you&#8217;ll know that the content creators on the site can only choose fields that the headless app supports. You can do that using this plugin I wrote:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/kellenmace\/gravityforms-fields-allowlist\">Gravity Forms Fields Allowlist<\/a><\/p>\n\n\n\n<p>Just modify the list of <code>ALLOWED_FIELDS<\/code> inside of <code>gravityforms-fields-allowlist.php<\/code> to include the ones you want to be displayed in the WordPress admin, then install and activate the plugin.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Can I Use This in My Gatsby Projects?<\/h2>\n\n\n\n<ol class=\"wp-block-list\" id=\"block-45f77617-c470-420d-a698-f245000e47fd\"><li>Copy and paste <code>src\/hooks\/useGravityForm.tsx<\/code>, and all the components inside of <code>src\/components<\/code> into your codebase.<\/li><li>Mimic the code in <code>gatsby-node.js<\/code> to create a new page and pass in a <code>formId<\/code> as context to that page component.<\/li><li>Copy the exported graphql query in <code>src\/templates\/questionnaire.tsx<\/code> and paste it into your page component file.<\/li><li>Inside of the page component that receives the <code>data.wpGravityFormsForm<\/code> prop, render the <code>&lt;GravityForm form={form} \/&gt;<\/code> component.<\/li><li>If you&#8217;re using TypeScript, set up GraphQL Code Generator to generate the TypeScript types stored in <code>generated\/graphql.ts<\/code> and used throughout this app. A <a href=\"https:\/\/youtu.be\/WUKo6frHsFw\">video<\/a> and <a href=\"https:\/\/wpengine.com\/builders\/work-with-gravity-forms-in-headless-wordpress\">blog post<\/a> are available that walk through this process.<\/li><\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping Up<\/h2>\n\n\n\n<p>Thanks for sticking with me! I hope this post and the code repos provided are helpful as you implement Gravity Forms forms in your headless WordPress + Gatsby applications. Please reach out and let us know how it goes!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A couple weeks ago, I wrote a Gravity Forms in Headless WordPress blog post, and recorded a YouTube video to accompany it. They were both well received, but several members [&hellip;]<\/p>\n","protected":false},"author":8,"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-211","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 Gatsby - Builders<\/title>\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-gatsby\/\" \/>\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 Gatsby - Builders\" \/>\n<meta property=\"og:description\" content=\"A couple weeks ago, I wrote a Gravity Forms in Headless WordPress blog post, and recorded a YouTube video to accompany it. They were both well received, but several members [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2021-06-14T19:44:04+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-02-06T03:01:37+00:00\" \/>\n<meta name=\"author\" content=\"Kellen Mace\" \/>\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=\"Kellen Mace\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 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-gatsby\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/\"},\"author\":{\"name\":\"Kellen Mace\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/e6e62698d757a8421cc9723ffa8b1be3\"},\"headline\":\"Gravity Forms in Headless WordPress with Gatsby\",\"datePublished\":\"2021-06-14T19:44:04+00:00\",\"dateModified\":\"2026-02-06T03:01:37+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/\"},\"wordCount\":2115,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/gravity-forms-gatsby-test-query-1024x708.png\",\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/\",\"name\":\"Gravity Forms in Headless WordPress with Gatsby - Builders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2021\\\/06\\\/gravity-forms-gatsby-test-query-1024x708.png\",\"datePublished\":\"2021-06-14T19:44:04+00:00\",\"dateModified\":\"2026-02-06T03:01:37+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#primaryimage\",\"url\":\"\",\"contentUrl\":\"\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/gravity-forms-in-headless-wordpress-gatsby\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Gravity Forms in Headless WordPress with Gatsby\"}]},{\"@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\\\/e6e62698d757a8421cc9723ffa8b1be3\",\"name\":\"Kellen Mace\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g\",\"caption\":\"Kellen Mace\"},\"description\":\"Kellen Mace is the Manager of the Developer Relations team at WP Engine. He likes building modern web apps with SvelteKit, TypeScript, Tailwind, and AI tools.\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/author\\\/kellen-macewpengine-com\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Gravity Forms in Headless WordPress with Gatsby - Builders","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-gatsby\/","og_locale":"en_US","og_type":"article","og_title":"Gravity Forms in Headless WordPress with Gatsby - Builders","og_description":"A couple weeks ago, I wrote a Gravity Forms in Headless WordPress blog post, and recorded a YouTube video to accompany it. They were both well received, but several members [&hellip;]","og_url":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/","og_site_name":"Builders","article_published_time":"2021-06-14T19:44:04+00:00","article_modified_time":"2026-02-06T03:01:37+00:00","author":"Kellen Mace","twitter_card":"summary_large_image","twitter_creator":"@wpebuilders","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Kellen Mace","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/"},"author":{"name":"Kellen Mace","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/e6e62698d757a8421cc9723ffa8b1be3"},"headline":"Gravity Forms in Headless WordPress with Gatsby","datePublished":"2021-06-14T19:44:04+00:00","dateModified":"2026-02-06T03:01:37+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/"},"wordCount":2115,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"image":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#primaryimage"},"thumbnailUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2021\/06\/gravity-forms-gatsby-test-query-1024x708.png","articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/","url":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/","name":"Gravity Forms in Headless WordPress with Gatsby - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"primaryImageOfPage":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#primaryimage"},"image":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#primaryimage"},"thumbnailUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2021\/06\/gravity-forms-gatsby-test-query-1024x708.png","datePublished":"2021-06-14T19:44:04+00:00","dateModified":"2026-02-06T03:01:37+00:00","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#primaryimage","url":"","contentUrl":""},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/gravity-forms-in-headless-wordpress-gatsby\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"Gravity Forms in Headless WordPress with Gatsby"}]},{"@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\/e6e62698d757a8421cc9723ffa8b1be3","name":"Kellen Mace","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/479f2598f161363ba2e78e1b085782477580ea3d5c4609cc9bce3be4945090d5?s=96&d=mm&r=g","caption":"Kellen Mace"},"description":"Kellen Mace is the Manager of the Developer Relations team at WP Engine. He likes building modern web apps with SvelteKit, TypeScript, Tailwind, and AI tools.","url":"https:\/\/wpengine.com\/builders\/author\/kellen-macewpengine-com\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/211","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\/8"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=211"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/211\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=211"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=211"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=211"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}