Programmatically create forms, and capture submissions with Next.js and GraphQL
Tags: graphql, javascript, react, forms
Published: 2020-06-17 13:49:31

Let's face it, forms are everywhere across the web, and they often take significant time to build depending on the requirements.

In this tutorial we will dynamically build pages with forms using Next.js, and GraphQL.

**Chapters:**

1. [Define a solid content model](#1-define-a-solid-content-model)
2. [Create the content model in GraphCMS](#2-create-the-models-in-graphcms)
3. [Create an example Page and Form with Fields as a content editor](#3-create-an-example-page-and-form-with-fields-as-a-content-editor)
4. [Reordering form fields](#4-reordering-form-fields)
5. [Query our Page, Form and Fields with GraphQL](#5-query-our-page-form-and-fields-with-graphql)
6. [Configure public API access](#6-configure-public-api-access)
7. [Setup Next.js project with dependencies](#7-setup-nextjs-project-with-dependencies)
8. [Build Pages programatically with Next.js](#8-build-pages-programatically-with-nextjs)
9. [Build our Form Field components](#9-build-our-form-field-components)
10. [Render our Form to our individual Pages](#10-render-our-form-to-our-individual-pages)
11. [Managing form state and submissions](#11-managing-form-state-and-submissions)
12. [Submitting our Form to GraphCMS with GraphQL Mutations](#12-submitting-our-form-to-graphcms-with-graphql-mutations)
13. [Deploy to Vercel](#13-deploy-to-vercel)

### TLDR;

- [Get the code](https://github.com/notrab/dynamic-graphql-nextjs-forms)

## 1. Define a solid content model

Before we dive into creating our schema, let's first think about what we're going to need to enable our marketing team to spin up landing page forms from just using the CMS.

It all starts with a Page. Pages must have a `slug` field so we can easily look up content from the params of any request.

Next, for simplicity, each page will have an associated `Form` model. For the sake of this tutorial, we'll pick 4 form field types;

* Input
* Textarea
* Select
* Checkbox

### Form Fields

If we think of a traditional form, let's try and replace all of the data points we need to recreate a simple contact form like the following:

```html
<form>
  <div>
    <label for="name">Name</label>
    <input type="text" id="name" placeholder="Your name" required />
  </div>
  <div>
    <label for="email">Email</label>
    <input type="email" id="email" placeholder="Your email" required />
  </div>
  <div>
    <label for="tel">Tel</label>
    <input type="tel" id="tel" placeholder="Your contact no." />
  </div>
  <div>
    <label for="favFramework">What's your favorite framework?</label>
    <select id="favFramework">
      <option value="react">React</option>
      <option value="vue">Vue</option>
      <option value="angular">Angular</option>
      <option value="svelte">Svelte</option>
    </select>
  </div>
  <div>
    <label for="message">Message</label>
    <textarea id="message" placeholder="Leave a message" />
  </div>
  <div>
    <label for="terms">
      <input id="terms" type="checkbox" />
      I agree to the terms and privacy policy.
    </label>
  </div>
  <div>
    <button type="submit">Submit</button>
  </div>
</form>
```

In the above form, we have some `<input />`'s that are required, some which are of type `email`, `tel` and `text`, while the `<select />` has no placeholder or is required.

GraphCMS has support for GraphQL Union Types. This means we can define models for each of our form field types, and associate them to our `Form` model as one "has many" field.

Our schema will end up looking a little something like the following...

### Models

#### `Page`

* **Title**, String, Single line text, Required, and used as a Title
* **Slug**, String, Single line text, Required
* **Form**, Reference to `Form`

#### `Form`

* **Page**, Reference, Accepts multiple `Page` values
* **Fields**, Reference, Accepts multiple `FormInput`, `FormTextarea`, `FormSelect` and `FormCheckbox` values

#### `FormInput`

* **Name**, String, Single line text, and used as a Title
* **Type**, Enum, `FormInputType` dropdown
* **Label**, String, Single line text
* **Placeholder**, Single line text
* **Required**, Boolean
* **Form**, Reference to `Form`

#### `FormTextarea`

* **Name**, String, Single line text, and used as a Title
* **Label**, String Single line text
* **Placeholder**, String, Single line text
* **Required**, Boolean
* **Form**, Reference to `Form`

#### `FormSelect`

* **Name**, String, Single line text, and used as a Title
* **Label**, String, Single line text
* **Required**, Boolean
* **Choices**, Reference, Accepts multiple `FormOption` values
* **Form**, Reference to `Form`

#### `FormOption`

* **Value**, String, Single line text, Required, and used as a Title
* **Option**, String, Single line text
* **FormSelect**, Reference, Belongs to `FormSelect`

#### `FormCheckbox`

* **Name**, String, Single line text, and used as a Title

* **Label**, String, Single line text, Required
* **Required**, Boolean
* **Form**, Reference to `Form`

### Enumerations

#### `FormInputType` values

* `EMAIL`
* `TEXT`
* `TEL`

🖐 _You could add more, but it's not required for this tutorial._

## 2. Create the models in GraphCMS

Now we have an idea of how our content model looks like. Let's create the models and their associations with eachother inside GraphCMS.

1. You'll need an account to continue. [Sign up](https://app.graphcms.com/signup) or head to the [Dashboard](https://app.graphcms.com).

2. Once logged in, head to the Schema editor by selecting **Schema** from the side.
3. Click **+ Add** in the sidebar above default system `Asset` model.
4. Go ahead and create the 7 models above. **Don't worry about creating relations just yet, you can do them all at once after creating the other fields.**

## 3. Create an example Page and Form with Fields as a content editor

So that we are able to query, and build our forms, we're going to need some content inside our models.

1. Inside the [Dashboard](https://app.graphcms.com), head to the Content editor by selecting **Content** from the side.
2. Select the **Page** model and click **+ Create New** from the top right.
3. Give your page a `title` and `slug`. I'll call use `Contact Us`, and `contact`, respectively.
4. Now underneath `Form`, click **Create and add a new form**.
5. Inside the inline `Form` content editor, click on **Create and add a new document**.
6. From the dropdown, select **FormInput**.
7. Inside the inline `FormInput` content editor, enter a `name`, `type` , `label` and `placeholder` for your form field. I'll add the values `Name`, `TEXT`, `Your name`, `Name` and set required to `true`.
8. Now click **Save and publish**.

**Repeat steps 5-8 to add additional fields.**

🖐 *To follow along with the rest of this tutorial, I will be using the following values for my fields...*

#### 3 x `FormInput`'s

- Name
  - Name: `name`
  - Type: `TEXT`
  - Label: `Name`
  - Placeholder: `Your name`
  - Required: `true`

- Email
  - Name: `email`
  - Type: `EMAIL`
  - Label: `Email`
  - Placeholder: `Your email`
  - Required: `true`
- Tel
  - Name: `tel`
  - Type: `TEL`
  - Label: `Tel`
  - Placeholder: `Your contact no.`
  - Required: `false`

#### 1 x `FormTextarea`

* Message
  * Name: `message`
  * Label: `Message`
  * Placeholder: `Leave a message`
  * Required: `true`

#### 1 x `FormCheckbox`

* Terms
  * Name: `terms`
  * Label: `I agree to the terms and privacy policy.`
  * Required: `true`

#### 1 x `FormSelect`

The `FormSelect` is a little special because it also references another model `FormSelect`.

First, create your `FormSelect` document as usual, entering the following.

* Favourite Framework
  * Name: `favFramework`
  * Label: `What's your favorite frontend framework?`
  * Required: `false`
* Next below **Options**, click on **Create and add a new formOption**.

Now for each of our choices below, repeat the steps to "*Create and add a new formOption*", and provide the `value`/`option` for each:

1. `react`/`React`
2. `vue`/`Vue`
3. `angular`/`Angular`
4. `svelte`/`Svelte`

Finally, click **Save and publish** on this and close each of the inline editors, making sure to publish any unsaved changes along the way.

## 4. Reordering form fields

Now we have created our fields, we can now reorder them using the content editor. This may be useful if you decide to add or remove some fields later, you can order the fields exactly the way you want them to appear.

✨ Simply drag each of the Field rows into the order you want. ✨

![Reordering GraphCMS Fields](https://dev-to-uploads.s3.amazonaws.com/i/5pujvnfnvy2zzk1lqrxr.png)

## 5. Query our Page, Form and Fields with GraphQL

We have two pages, with two separate forms:

* Contact Form
* Request a Demo

Let's start by querying for all pages and their forms using the **API Playground** available from the sidebar within your project [Dashboard](https://app.graphcms.com).

### Query pages, form and field `__typename`

```graphql
{
  pages {
    title
    slug
    form {
      id
      fields {
        __typename
      }
    }
  }
}
```

### Union Type Query

As we're using Union Types for our form `fields`, we must use the `... on TypeName` notation to query each of our models.

Let's go ahead and query `on` all of our models we created earlier.

```graphql
{
  pages {
    title
    slug
    form {
      id
      fields {
        __typename
        ... on FormInput {
          name
          type
          inputLabel: label
          placeholder
          required
        }
        ... on FormTextarea {
          name
          textareaLabel: label
          placeholder
          required
        }
        ... on FormCheckbox {
          name
          checkboxLabel: label
          required
        }
        ... on FormSelect {
          name
          selectLabel: label
          options {
            value
            option
          }
          required
        }
      }
    }
  }
}
```

The response should look a little something like the following:

```json
{
  "data": {
    "pages": [
      {
        "title": "Contact us",
        "slug": "contact",
        "form": {
          "id": "ckb9j9y3k004i0149ypzxop4r",
          "fields": [
            {
              "__typename": "FormInput",
              "name": "Name",
              "type": "TEXT",
              "inputLabel": "Name",
              "placeholder": "Your name",
              "required": true
            },
            {
              "__typename": "FormInput",
              "name": "Email",
              "type": "EMAIL",
              "inputLabel": "Email address",
              "placeholder": "you@example.com",
              "required": true
            },
            {
              "__typename": "FormInput",
              "name": "Tel",
              "type": "TEL",
              "inputLabel": "Phone no.",
              "placeholder": "Your phone number",
              "required": false
            },
            {
              "__typename": "FormSelect",
              "name": "favFramework",
              "selectLabel": "What's your favorite frontend framework?",
              "options": [
                {
                  "value": "React",
                  "option": "React"
                },
                {
                  "value": "Vue",
                  "option": "Vue"
                },
                {
                  "value": "Angular",
                  "option": "Angular"
                },
                {
                  "value": "Svelte",
                  "option": "Svelte"
                }
              ],
              "required": false
            },
            {
              "__typename": "FormTextarea",
              "name": "Message",
              "textareaLabel": "Message",
              "placeholder": "How can we help?",
              "required": true
            },
            {
              "__typename": "FormCheckbox",
              "name": "Terms",
              "checkboxLabel": "I agree to the terms and privacy policy.",
              "required": true
            }
          ]
        }
      }
    ]
  }
}
```

## 6. Configure public API access

GraphCMS has a flexible permissions system, which includes enabling certain user groups to do actions, and most importantly restrict who can query what data.

For the purposes of querying data to build our pages and forms, we'll enable public API queries.

To do this, go to your project **Settings**.

1. Open the **API Access** page
2. Enable **Content from stage Published** under **Public API permissions**
3. **Save** ✨

That's it! You can test this works using the **API Playground** and selecting `Environment: master Public` from the dropdown in the section above your query/result.

<img src="https://media.graphcms.com/LM1rY7KmSXaHP95QaYeI" alt="API Playground Environment dropdown" style="zoom:50%;" />

🖐 **Make sure to copy  your API Endpoint to the clipboard. We'll need it in step 8.**

## 7. Setup Next.js project with dependencies

Now we have our schema, and content, let's begin creating a new Next.js project with all of the dependencies we'll need to build our pages and forms.

Inside the Terminal, run the following to create a new Next.js project.

```bash
npm init next-app dynamic-graphcms-forms
```

**When prompted, select `Default starter app` from the template choices.**

```bash
cd dynamic-graphcms-forms
```

This template will scaffold a rough folder structure following Next.js best practices.

Next, we'll install `graphql-request` for making GraphQL queries via fetch.

```bash
yarn add -E graphql-request # or npm install ...
```

Now, if you run the project, you should see the default Next.js welcome page at [`http://localhost:3000`](http://localhost:3000).

```bash
yarn dev # or npm run dev
```

## 8. Build Pages programatically with Next.js

This comes in two significant parts. First we create the routes (or "paths") and then query for the data for each page with those path params.

### 8.1 Create programmatic page routes

First up is to add some code to our Next.js application that will automatically generate pages for us. For this we will be exporting the `getStaticPaths` function from a new file called `[slug].js` in our `pages` directory.

```bash
touch pages/[slug].js
```

Having a filename with square brackets may look like a typo, but rest assured this is a Next.js convention.

Inside `pages/[slug].js` add the following code to get going:

```jsx
export default function Index(props) {
  return (
    <pre>{JSON.stringify(props, null, 2)}</pre>
  )
}
```

If you're familiar with React already, you'll notice we are destructuring `props` from the `Index` function. We'll be updating this later to destructure our individual page data, but for now, we'll show the `props` data on each of our pages.

Inside `pages/[slug].js`, let's import `graphql-request` and initialize a new `GraphQLClient` client.

🖐 **You'll need your API Endpoint from Step 6 to continue.**

```js
import { GraphQLClient } from "graphql-request";

const graphcms = new GraphQLClient("YOUR_GRAPHCMS_ENDOINT_FROM_STEP_6");
```

Now the `graphcms` instance, we can use the `request ` function to send queries (with variables) to GraphCMS.

Let's start by querying for all pages, and get their slugs, inside a new exported function called `getStaticPaths`.

```js
export async function getStaticPaths() {
  const { pages } = await graphcms.request(`{
		pages {
			slug
		}
	}`)
  
  return {
    paths: pages.map(({ slug }) => ({ params: { slug } })),
    fallback: false
  }
}
```

There's quite a bit going on above, so let's break it down...

```js
const { pages } = await graphcms.request(`{
	pages {
		slug
	}
}`)
```

Here we are making a query and destructuring the response `pages` from the request. This will be similar to the results we got back in step 5.

```js
return {
  paths: pages.map(({ slug }) => ({ params: { slug } })),
  fallback: false
}
```

Finally inside `getStaticPaths` we are returning `paths` for our pages, and a `fallback`. These build the dynamic paths inside the root `pages` directory, and each of the slugs will become `pages/[slug].js`.

*The `fallback` is `false` in this example, but you can read more about using that here.*

🖐  `getStaticPaths` alone does nothing, we need to next query data for each of the pages.

### 8.2 Query page data

Now we have programmatic paths being generated for our pages, it's now time to query the same data we did in step 5, but this time, send that data to our page.

Inside `pages/[slug].js`, export the following function:

```js
export async function getStaticProps({ params: variables }) {
  const { page } = await graphcms.request(
    `query page($slug: String!) {
      page(where: {slug: $slug}) {
        title
        slug
        form {
          fields {
            __typename
            ... on FormInput {
              name
              type
              inputLabel: label
              placeholder
              required
            }
            ... on FormTextarea {
              name
              textareaLabel: label
              placeholder
              required
            }
            ... on FormCheckbox {
              name
              checkboxLabel: label
              required
            }
            ... on FormSelect {
              name
              selectLabel: label
              options {
                value
                option
              }
              required
            }
          }
        }
      }
    }
    `,
    variables
  );

  return {
    props: {
      page,
    },
  };
}
```

Now just like before, there's a lot going on, so let's break it down...

```js
export async function getStaticProps({ params: variables }) {
  // ... 
}
```

Here we are destructuring the `params` object from the request sent to our page. The params here will be what we sent in `getStaticPaths`, so we'd expect to see `slug` here.

🖐 As well as destructuring, we are also renaming (or reassigning) the variable `params` to `variables`.

```js
const { page } = await graphcms.request(`...`, variables);

return {
  props: {
    page,
  },
};
```

Next we're sending the same query we did in step 5, but this time we've given the query a name `page` which expects the `String` variable `slug`.

Once we send on our renamed `params` as `variables`, we return an object with our `page` inside of `props`.

Now all that's left to do is run our Next development server and see our response JSON on the page!

```bash
yarn dev # or npm run dev
```

Now you should see at [`http://localhost:3000/contact`](http://localhost:3000/contact) the data from GraphCMS for our Page.

<img src="https://media.graphcms.com/Y4PHtg3NT3CZhe3apRXQ" alt="Next development server" style="zoom:50%;" />

## 9. Build our Form Field components

We are now ready to dynamically build our form using the data from GraphCMS.

The `__typename` value will come in handy when rendering our form, as this will decide which component gets renderered.

Inside a new directory `components`, add a `Form.js` file.

```bash
mkdir components
touch components/Form.js
```

In this this file, we will create the structure of our basic form, and `map` through each of our `fields` to return the appropreciate field.

**Add the following code to `components/Form.js`**

```jsx
import * as Fields from "./FormFields";

export default function Form({ fields }) {
  if (!fields) return null;

  return (
    <form>
      {fields.map(({ __typename, ...field }, index) => {
        const Field = Fields[__typename];

        if (!Field) return null;

        return <Field key={index} {...field} />;
      })}

      <button type="submit">Submit</button>
    </form>
  );
}
```

Once you have this component setup, now create the file `components/FormFields/index.js` and add the following:

```jsx
export { default as FormCheckbox } from "./FormCheckbox";
export { default as FormInput } from "./FormInput";
export { default as FormSelect } from "./FormSelect";
export { default as FormTextarea } from "./FormTextarea";
```

All we're doing in this file is importing each of our different form fields and exporting them.

The reason we do this is that when we import using `import * as Fields`, we can grab any of the named exports by doing `Fields['FormCheckbox']`, or `Fields['FormInput']` like you see in `components/Form.js`.

Now that that we are importing these new fields, we next need to create each of them!

For each of the imports above, create new files inside `components/FormFields` for:

* `FormCheckbox.js`
* `FormInput.js`
* `FormSelect.js`
* `FormTextarea.js`

Once these are created, let's export each of the components as default, and write a minimum amount of code to make them work.

**The code in the below files isn't too important. What's key about this tutorial is how we can very easily construct forms, and in fact any component or layout, using just data from the CMS. Magic! ✨**

#### `FormCheckbox.js`

```jsx
export default function FormCheckbox({ checkboxLabel, ...rest }) {
  const { name } = rest;

  return (
    <div>
      <label htmlFor={name}>
        <input id={name} type="checkbox" {...rest} />
        {checkboxLabel || name}
      </label>
    </div>
  );
}
```

#### `FormInput.js`

Since this component acts as a generic `<input />`, we will need to lowercase the `type` enumeration to pass to the input.

```jsx
export default function FormInput({ inputLabel, type: enumType, ...rest }) {
  const { name } = rest;
  const type = enumType.toLowerCase();

  return (
    <div>
      {inputLabel && <label htmlFor={name}>{inputLabel || name}</label>}
      <input id={name} type={type} {...rest} />
    </div>
  );
}
```

#### `FormSelect.js`

```jsx
export default function FormSelect({ selectLabel, options, ...rest }) {
  const { name } = rest;

  if (!options) return null;

  return (
    <div>
      <label htmlFor={name}>{selectLabel || name}</label>
      <select id={name} {...rest}>
        {options.map(({ option, ...opt }, index) => (
          <option key={index} {...opt}>
            {option}
          </option>
        ))}
      </select>
    </div>
  );
}
```

#### `FormTextarea.js`

```jsx
export default function FormTextarea({ textareaLabel, ...rest }) {
  const { name } = rest;

  return (
    <div>
      <label htmlFor={name}>{textareaLabel || name}</label>
      <textarea id={name} {...rest} />
    </div>
  );
}
```

We're done on the form components, for now...!

## 10. Render our Form to our individual Pages

Let's recap...

* We have our form model and content coming from GraphCMS
* We have our form fields created
* We have our form pages automatically created

Let's now render the form we created in step 9 to our page.

Inside `pages/[slug].js`, we'll need to import our Form component and return that inside of the default export.

Below your current import (`graphql-request`), import our Form component:

```jsx
import Form from "../components/Form";
```

Lastly, update the default export to return the `<Form />`.

```jsx
export default function Index({ page }) {
  const { form } = page;

  return <Form {...form} />;
}
```

Next run the Next.js development server:

```bash
yarn dev # or npm run dev
```

Once the server has started, head to [`http://localhost:3000/contact`](http://localhost:3000/contact) (or a `slug` you defined in the CMS) to see your form!

<img src="https://media.graphcms.com/1zQiw33T8mHwhSeQpvg8" alt="Form Components" style="zoom:50%;" />

*I'll leave the design and UI aesthetics up to you!*

---

As far as creating dynamic forms with React, Next.js and GraphQL goes, this is it! Next we'll move onto enhancing the form to be accept submissions.

## 11. Managing form state and submissions

In this step we will install a library to handle our form state, and submissions, as well as create an `onSubmit`  that'll we'll use in Step 12 to forward onto GraphCMS.

Inside the terminal, let's install a new dependency:

```bash
yarn add -E react-hook-form # or npm install ...
```

Now it's not essential we use `react-hook-form` for managing our form, I wanted to provide a little closer to real world scenario than your typical `setState` example that are used in tutorials.

After we complete this tutorial, you should be in a position to return to each of your form fields, add some CSS, error handling, and more, made easy with `react-hook-form`!

Inside `components/Form.js`, add the following import to the top of the file:

```jsx
import { useForm, FormContext } from "react-hook-form";
```

Then inside your `Form` function after you `return null` if there are no `fields`, add the following:

```jsx
const { handleSubmit, ...methods } = useForm();

const onSubmit = (values) => console.log(values);
```

Finally, you'll need to wrap the current `<form>` with `<FormContext {...methods}>`, and add a `onSubmit` prop to the `<form>` that is `onSubmit={handleSubmit(onSubmit)}`.

Your final `components/Form.js` should look like this:

```jsx
import { useForm, FormContext } from "react-hook-form";

import * as Fields from "./FormFields";

export default function Form({ fields }) {
  if (!fields) return null;

  const { handleSubmit, ...methods } = useForm();

  const onSubmit = (values) => console.log(values);

  return (
    <FormContext {...methods}>
      <form onSubmit={handleSubmit(onSubmit)}>
        {fields.map(({ __typename, ...field }, index) => {
          const Field = Fields[__typename];

          if (!Field) return null;

          return <Field key={index} {...field} />;
        })}

        <button type="submit">Submit</button>
      </form>
    </FormContext>
  );
}
```

Now all that's happening here is we're initializing a new `react-hook-form` instance, and adding a `FormContext` provider around our form + fields.

Next we'll need to update each of our `FormFields/*.js` and `register` them with the `react-hook-form` context.

First update `components/FormFields/FormInput.js` to include the hook `useFormContext` from `react-hook-form`.

At the top of the file add the following import:

```jsx
import { useFormContext } from 'react-hook-form'
```

Then inside the `FormInput` function, add the following before the `return`:

```jsx
const { register } = useFormContext();
```

Now all that's left to do add `register` as a `ref` to our `<input />` and pass in the `required` value.

```jsx
<input
  ref={register({ required: rest.required })}
  id={name}
  type={type}
  {...rest}
/>
```

**The final `FormInput` should look like:**

```jsx
import { useFormContext } from "react-hook-form";

export default function FormInput({ inputLabel, type: enumType, ...rest }) {
  const { register } = useFormContext();
  const { name } = rest;
  const type = enumType.toLowerCase();

  return (
    <div>
      {inputLabel && <label htmlFor={name}>{inputLabel || name}</label>}
      <input
        ref={register({ required: rest.required })}
        id={name}
        type={type}
        {...rest}
      />
    </div>
  );
}

```

---

Great! Now let's do the same for the other 3 field components:

#### `FormCheckbox.js`

```jsx
import { useFormContext } from "react-hook-form";

export default function FormCheckbox({ checkboxLabel, ...rest }) {
  const { register } = useFormContext();
  const { name } = rest;

  return (
    <div>
      <label htmlFor={name}>
        <input
          ref={register({ required: rest.required })}
          id={name}
          type="checkbox"
          {...rest}
        />
        {checkboxLabel || name}
      </label>
    </div>
  );
}
```

#### `FormSelect.js`

```jsx
import { useFormContext } from "react-hook-form";

export default function FormSelect({ selectLabel, options, ...rest }) {
  if (!options) return null;

  const { register } = useFormContext();
  const { name } = rest;

  return (
    <div>
      <label htmlFor={name}>{selectLabel || name}</label>
      <select ref={register({ required: rest.required })} id={name} {...rest}>
        {options.map(({ option, ...opt }, index) => (
          <option key={index} {...opt}>
            {option}
          </option>
        ))}
      </select>
    </div>
  );
}
```

#### `FormTextarea.js`

```jsx
import { useFormContext } from "react-hook-form";

export default function FormTextarea({ textareaLabel, ...rest }) {
  const { register } = useFormContext();
  const { name } = rest;

  return (
    <div>
      <label>{textareaLabel || name}</label>
      <textarea
        ref={register({ required: rest.required })}
        htmlFor={name}
        id={name}
        {...rest}
      />
    </div>
  );
}
```

---

🖐 Let's start the Next.js development server, and view the console when we submit the form!

```bash
yarn dev # or npm run dev
```

Once the server has started, head to [`http://localhost:3000/contact`](http://localhost:3000/contact) (or a `slug` you defined in the CMS) to see your form!

Open the browser developer tools console, and then **fill out the form** and **click submit**!

You should now see the form values submitted!

![Developer Tools Console](https://media.graphcms.com/GMjE2UUJTGKTogJxgX9e)

## 12. Submitting our Form to GraphCMS with GraphQL Mutations

It's now time to take our form to the next level. We are going to update our GraphCMS schema with a new `Submission` model that will be used to store submissions.

Inside the GraphCMS Schema Editor, click **+ Add** to create a new model.

* Give the model a name of `Submission`,
* Add a new JSON Editor field with the Display Name `Form Data`, and, API ID as `formData`,
* Add a new Reference field with the Display Name/API ID `Form`/`form` , and select `Form` as the **Model that can be referenced**,
* Configure the reverse field to **Allow multiple values** and set the default Display Name/API ID to (`Submissions`/`submissions`) respectively.

Things should look a little something like the following:

![Submission model](https://media.graphcms.com/el0FfipQaa3slIDZuqvJ)

And the `Form` model should now have a new field `submisson`:

![Form Model](https://media.graphcms.com/Jh6oakWcQuSYnIpjQ23a)

Since we want full control via the CMS what appears on our form, we'll just save all of that data inside `formData` JSON field.

🖐 Using something like webhooks would enable you to forward `formData` onto a service like Zapier, and do what you need to with the data, all without writing a single line of code! ✨

In order to use the Mutations API, we'll need to configure our API access to permit mutations and create a dedicated Permanent Auth Token. **Don't enable Mutations for the Public API, as anybody will be able to query/mutate your data**!

Head to `Settings > API Access > Permanent Auth Tokens` and create a token with the following setup:

<img src="https://media.graphcms.com/ShU8PnJeRvewbxOpmwec" alt="Create Permanent Auth Token" style="zoom:50%;" />

**Next, `Copy` the token to the clipboard once created.**

Inside of the root of your Next.js project, create the file `.env` and, add the following, replacing `YOUR_TOKEN_HERE` with your token:

```pseudocode
GRAPHCMS_MUTATION_TOKEN=YOUR_TOKEN_HERE
```

With this token added, let's also do some housekeeping. Replace the API Endpoint you created in`/pages/[slug].js` with a the `.env` variable `GRAPHCMS_ENDPOINT` and assign the value inside `.env`:

```js
// pages/[slug].js

// ...
const graphcms = new GraphQLClient(process.env.GRAPHCMS_ENDPOINT);
// ...
```

Now before we can use the `GRAPHCMS_MUTATION_TOKEN`, we'll need to update our `components/Form/index.js` to `POST` the values to a Next.js API route.

Inside the form, let's do a few things:

* import `useState` from React,
* Invoke `useState` inside your `Form` function,
* Replace the `onSubmit` function,
* Render `error` after the submit `<button />`

```jsx
import { useState } from 'react'

// ...
export default function Form({ fields }) {
  if (!fields) return null;

  const [success, setSuccess] = useState(null);
  const [error, setError] = useState(null);

  // ...
  
  const onSubmit = async (values) => {
    try {
      const response = await fetch("/api/submit", {
        method: "POST",
        body: JSON.stringify(values),
      });

      if (!response.ok)
        throw new Error(`Something went wrong submitting the form.`);

      setSuccess(true);
    } catch (err) {
      setError(err.message);
    }
  };
  
  if (success) return <p>Form submitted. We'll be in touch!</p>;
  
  return (
    // ...
  	<button type="submit">Submit</button>
  	{error && <span>{error}</span>}}
  )
}
```

---

Finally we'll create the API route `/api/submit` that forwards requests to GraphCMS securely. **We need to do this to prevent exposing our Mutation Token to the public.**

One of the best ways to scaffold your mutation is to use the API Playground inside your GraphCMS project. It contains all of the documentation and types associated with your project/models.

<img src="https://media.graphcms.com/xfT3CcbPQzKEBWsJY1SJ" alt="API Playground" style="zoom:50%;" />

If you've followed along so far, the following mutation is all we need to create + connect form submissions.

```graphql
mutation createSubmission($formData: Json!, $formId: ID!) {
  createSubmission(data: {formData: $formData, form: {connect: {id: $formId}}}) {
    id
  }
}
```

The `createSubmission` mutation takes in 2 arguments; `formData` and `formId`.

**In the `onSubmit` function above, we're passing along `values` which will be our `formData`. All we need to do now is pass along the form ID!**

We are already querying for the form `id` inside `pages/[slug].js`, so we can use this `id` passed down to the `Form` component.

Inside `components/Form.js`, destructure `id` when declaring the function:

```jsx
export default function Form({ id, fields }) {
  // ...
}
```

.... and then pass that `id` into the `onSubmit` `body`:

```js
const response = await fetch("/api/submit", {
  method: "POST",
  body: JSON.stringify({ id, ...values }),
});
```

Then, inside the `pages` directory, create the directory/file `api/submit.js`, and add the following code:

```js
import { GraphQLClient } from "graphql-request";

export default async ({ body }, res) => {
  const { id, ...data } = JSON.parse(body);

  const graphcms = new GraphQLClient(process.env.GRAPHCMS_ENDPOINT, {
    headers: {
      authorization: `Bearer ${process.env.GRAPHCMS_MUTATION_TOKEN}`,
    },
  });

  try {
    const { createSubmission } = await graphcms.request(`
      mutation createSubmission($data: Json!, $id: ID!) {
        createSubmission(data: {formData: $data, form: {connect: {id: $id}}}) {
          id
        }
      }`,
      {
        data,
        id,
      }
    );

    res.status(201).json(createSubmission);
  } catch ({ message }) {
    res.status(400).json({ message });
  }
};
```

That's it! ✨

Now go ahead and submit the form, open the content editor and navigate to the `Submission` content.

You should see your new entry!

![Submission entries](https://dev-to-uploads.s3.amazonaws.com/i/tzq1ogiif9rg4xpsk8da.png)

You could use GraphCMS webhooks to listen for new submissions, and using another API route forward that onto a service of your choice, such as email, Slack or Zapier.

## 13. Deploy to Vercel

Now all that's left to do is deploy our Next.js site to [Vercel](https://vercel.com). Next.js is buil, and managed by the Vercel team and the community.

To deploy to Vercel, you'll need to install the CLI.


```bash
npm i -g vercel # or yarn global add vercel
```

Once installed, all it takes to deploy is one command!

```bash
vercel # or vc
```

You'll next be asked to confirm whether you wish to deploy the current directory, and what the project is named, etc. The defaults should be enough to get you going! 😅

Once deployed, you'll get a URL to your site. Open the deployment URL and append `/contact` to see your form!