Documentation
Feedback
Guides
Storefront Development

Storefront Development
FastStoreExtending the FastStore APIAPI extensions - Use cases
Adding a contact form to a landing page
This guide describes how to handle data from a Contact Us form and send it to a third-party API. To illustrate this, we will create a Contact Us page with a Contact form.
For detailed instructions on API extensions, see the API extensions guide.

Context

  • You want to create a Contact Us page so shoppers can contact your store.
  • You want to add a form so shoppers can send their requests or feedback.
  • To handle the data submitted through this contact form, you need to extend the FastStore API with third-party API schemas to handle the data that comes from the form.

Implementation

Creating GraphQL files

First, you need to set up the necessary GraphQL files to handle data submissions.
  1. In your store repository, go to the src folder. If you don’t have it, create a new graphql folder.
  2. Inside graphql, create the thirdParty folder.
  3. In the thirdParty folder, create two subfolders:
    • resolvers
    • typeDefs
  4. Create an index.ts file inside the resolvers folder to set up the base structure for your resolver functions.
For more details about code implementation, see the thirdParty folder available in the playground.store repository.

_10
src
_10
┗ 📂 graphql
_10
┗ 📂thirdParty
_10
┣ 📂 resolvers
_10
┗ 📄index.ts
_10
┗ 📂 typeDefs

Defining the types

Next, define the GraphQL types for your contact form in a file called contactForm.graphql inside the thirdParty folder. This file will contain type definitions for your GraphQL schema.
  1. In the graphql/thirdParty/resolvers folder, create a contactForm.ts file to handle the resolver logic for your contact form.
  2. In the graphql/thirdParty/typeDefs folder, create a contactForm.graphql file and add the following schema definitions.
  • ContactFormResponse: Defines the structure of the response from the API, with a mandatory message field.
  • ContactFormInput: Specifies the input fields required for the contact form.
  • Mutation: Declares a mutation for submitting the contact form data.
contactForm.graphql

_14
type ContactFormResponse {
_14
message: String!
_14
}
_14
_14
input ContactFormInput {
_14
name: String!
_14
email: String!
_14
subject: String!
_14
message: String!
_14
}
_14
_14
type Mutation {
_14
submitContactForm(input: ContactFormInput!): ContactFormResponse
_14
}

Creating the resolvers

Now, let's create the resolver function to process the form submission.
In the contactForm.ts file, add the following code. This file imports the contactFormResolver and combines it with other potential resolvers into a single object.
contactForm.graphql
contactForm.ts

_39
type SubmitContactFormData = {
_39
input: {
_39
name: string;
_39
email: string;
_39
subject?: string;
_39
message: string;
_39
};
_39
};
_39
_39
const contactFormResolver = {
_39
Mutation: {
_39
submitContactForm: async (_: never, data: SubmitContactFormData) => {
_39
const { input } = data;
_39
_39
try {
_39
const response = await fetch(
_39
"https://playground.vtexcommercestable.com.br/api/dataentities/ContactForm/documents?_schema=contactForm",
_39
{
_39
method: "POST",
_39
headers: {
_39
"Content-Type": "application/json",
_39
},
_39
body: JSON.stringify(input),
_39
}
_39
);
_39
_39
if (!response.ok) {
_39
throw new Error("Error while sending the message");
_39
}
_39
_39
return { message: "Your message was sent successfully!" };
_39
} catch (error) {
_39
return { message: error };
_39
}
_39
},
_39
},
_39
};
_39
_39
export default contactFormResolver;

Consolidating the resolvers

In the graphql/thirdParty/resolvers folder, create an index.ts file to consolidate the resolvers:
contactForm.graphql
contactForm.ts
index.ts

_10
import contactFormResolver from "./contactForm";
_10
_10
const resolvers = {
_10
...contactFormResolver,
_10
};
_10
_10
export default resolvers;

Creating a new section

Create a new section to receive the Contact Form data.
  1. In the src/components folder, create the ContactForm folder.
  2. In the ContactForm folder, create the following files:
    • ContactForm.tsx: The main component file.
    • contant-form.module.scss: The stylesheet for the component.
  3. Add the following code to the ContactForm.tsx.
  • This component renders a contact form with name, email, subject, and message fields.
  • The onSubmit function handles form submission, sends the data to the server, and clears the form fields upon success.
contactForm.graphql
contactForm.ts
index.ts
ContactForm.tsx

_100
import { useCallback, useState } from "react";
_100
import { gql } from "@faststore/core/api";
_100
import { useLazyQuery_unstable as useLazyQuery } from "@faststore/core/experimental";
_100
_100
import {
_100
InputField as UIInputField,
_100
Button as UIButton,
_100
TextArea as UITextArea,
_100
} from "@faststore/ui";
_100
_100
import styles from "./contact-form.module.scss";
_100
_100
export const mutation = gql(`
_100
mutation SubmitContactForm($data: ContactFormInput!) {
_100
submitContactForm(input: $data) {
_100
message
_100
}
_100
}
_100
`);
_100
_100
export const ContactForm = () => {
_100
const [submitContactForm, { data, error }] = useLazyQuery(mutation, {
_100
data: { name: "", email: "", subject: "", message: "" },
_100
});
_100
_100
const [name, setName] = useState("");
_100
const [email, setEmail] = useState("");
_100
const [subject, setSubject] = useState("");
_100
const [message, setMessage] = useState("");
_100
_100
const onSubmit = useCallback(
_100
(event: React.FormEvent<HTMLFormElement>) => {
_100
event.preventDefault();
_100
_100
const formValues = {
_100
name,
_100
email,
_100
subject,
_100
message,
_100
};
_100
_100
submitContactForm({ data: formValues });
_100
_100
if (error) {
_100
console.error(error);
_100
}
_100
_100
if (data) {
_100
setName("");
_100
setEmail("");
_100
setSubject("");
_100
setMessage("");
_100
}
_100
},
_100
[submitContactForm]
_100
);
_100
_100
return (
_100
<section className={styles.contactForm}>
_100
<div>
_100
<h2>Contact Us</h2>
_100
<p>
_100
Need to get in touch with us? Please fill out the form, we'll get in
_100
touch with you soon.
_100
</p>
_100
</div>
_100
<form onSubmit={onSubmit}>
_100
<UIInputField
_100
id="name"
_100
label="Name"
_100
value={name}
_100
onChange={(e) => setName(e.target.value)}
_100
/>
_100
<UIInputField
_100
id="email"
_100
label="Email"
_100
value={email}
_100
onChange={(e) => setEmail(e.target.value)}
_100
/>
_100
<UIInputField
_100
id="subject"
_100
label="Subject"
_100
value={subject}
_100
onChange={(e) => setSubject(e.target.value)}
_100
/>
_100
<UITextArea
_100
id="message"
_100
placeholder="Write your message here."
_100
value={message}
_100
onChange={(e) => setMessage(e.target.value)}
_100
/>
_100
<UIButton type="submit" variant="primary">
_100
Send
_100
</UIButton>
_100
</form>
_100
</section>
_100
);
_100
};
_100
_100
export default ContactForm;

Creating a stylesheet for the section

In the contact-form.module.scss file, add the following code. The stylesheet applies specific styles to the Contact Form component, including layout and spacing adjustments.
For more information about code implementation, see the ContactForm folder available in the playground.store repository.
contactForm.graphql
contactForm.ts
index.ts
ContactForm.tsx
contact-form.module.scss

_44
.contactForm {
_44
@import "@faststore/ui/src/components/atoms/Button/styles.scss";
_44
@import "@faststore/ui/src/components/atoms/Input/styles.scss";
_44
@import "@faststore/ui/src/components/molecules/InputField/styles.scss";
_44
_44
@include layout-content;
_44
_44
display: grid;
_44
grid-template-columns: 1fr 1fr;
_44
column-gap: var(--fs-spacing-2);
_44
_44
> div,
_44
form {
_44
margin: var(--fs-spacing-6) 0 var(--fs-spacing-3);
_44
}
_44
_44
> div {
_44
p {
_44
width: 80%;
_44
}
_44
}
_44
_44
h2 {
_44
font-family: var(--fs-text-face-title);
_44
font-size: var(--fs-text-size-title-page);
_44
margin: var(--fs-spacing-6) 0 var(--fs-spacing-3);
_44
}
_44
_44
[data-fs-input-field] {
_44
margin: var(--fs-spacing-3) 0;
_44
}
_44
_44
[data-fs-textarea] {
_44
width: 100%;
_44
margin-bottom: var(--fs-spacing-4);
_44
padding: var(--fs-spacing-2) var(--fs-spacing-2) 0;
_44
height: 150px;
_44
}
_44
_44
[data-fs-button] {
_44
min-width: 250px;
_44
margin: auto;
_44
}
_44
}

Synchronizing the changes with the Headless CMS

Add the section to Headless CMS by following the instructions available in Syncing components with Headless CMS.
The following schema was used as an example.
For more information about code implementation, see the sections.json file available in the playground.store repository.
contactForm.graphql
contactForm.ts
index.ts
ContactForm.tsx
contact-form.module.scss
sections.json

_11
[
_11
{
_11
"name": "ContactForm",
_11
"schema": {
_11
"title": "ContactForm",
_11
"description": "Adds a contact form",
_11
"type": "object",
_11
"properties": {}
_11
}
_11
}
_11
]

Creating GraphQL files

First, you need to set up the necessary GraphQL files to handle data submissions.
  1. In your store repository, go to the src folder. If you don’t have it, create a new graphql folder.
  2. Inside graphql, create the thirdParty folder.
  3. In the thirdParty folder, create two subfolders:
    • resolvers
    • typeDefs
  4. Create an index.ts file inside the resolvers folder to set up the base structure for your resolver functions.
For more details about code implementation, see the thirdParty folder available in the playground.store repository.

Defining the types

Next, define the GraphQL types for your contact form in a file called contactForm.graphql inside the thirdParty folder. This file will contain type definitions for your GraphQL schema.
  1. In the graphql/thirdParty/resolvers folder, create a contactForm.ts file to handle the resolver logic for your contact form.
  2. In the graphql/thirdParty/typeDefs folder, create a contactForm.graphql file and add the following schema definitions.
  • ContactFormResponse: Defines the structure of the response from the API, with a mandatory message field.
  • ContactFormInput: Specifies the input fields required for the contact form.
  • Mutation: Declares a mutation for submitting the contact form data.

Creating the resolvers

Now, let's create the resolver function to process the form submission.
In the contactForm.ts file, add the following code. This file imports the contactFormResolver and combines it with other potential resolvers into a single object.

Consolidating the resolvers

In the graphql/thirdParty/resolvers folder, create an index.ts file to consolidate the resolvers:

Creating a new section

Create a new section to receive the Contact Form data.
  1. In the src/components folder, create the ContactForm folder.
  2. In the ContactForm folder, create the following files:
    • ContactForm.tsx: The main component file.
    • contant-form.module.scss: The stylesheet for the component.
  3. Add the following code to the ContactForm.tsx.
  • This component renders a contact form with name, email, subject, and message fields.
  • The onSubmit function handles form submission, sends the data to the server, and clears the form fields upon success.

Creating a stylesheet for the section

In the contact-form.module.scss file, add the following code. The stylesheet applies specific styles to the Contact Form component, including layout and spacing adjustments.
For more information about code implementation, see the ContactForm folder available in the playground.store repository.

Synchronizing the changes with the Headless CMS

Add the section to Headless CMS by following the instructions available in Syncing components with Headless CMS.
The following schema was used as an example.
For more information about code implementation, see the sections.json file available in the playground.store repository.

_10
src
_10
┗ 📂 graphql
_10
┗ 📂thirdParty
_10
┣ 📂 resolvers
_10
┗ 📄index.ts
_10
┗ 📂 typeDefs

Creating a new landing page

Let’s create a new landing page for the Contact Us page to add the new Contact Form section. For this part, we will follow the Creating a new page tutorial.
  1. Go to the VTEX Admin and access Storefront > Headless CMS.
  2. Click Create document and select Landing Page.
  3. In the Sections tab, click add (+) and choose the ContactForm section.
  4. Go to the Settings tab and add the following path in the Path field: /contact-us.
  5. Click Save.

Results

Once you have set your development mode to see the changes locally, access the https://localhost:3000/contact-us and you will see the new landing page with the Contact Us form. Check the storefront example below:
{"base64":"  ","img":{"width":1999,"height":1003,"type":"png","mime":"image/png","wUnits":"px","hUnits":"px","length":91243,"url":"https://vtexhelp.vtexassets.com/assets/docs/src/api-extension-example-one___ceffcf6726b7316bbe61eaf4c013e069.png"}}
Contributors
1
Photo of the contributor
+ 1 contributors
Was this helpful?
Yes
No
Suggest Edits (GitHub)
Contributors
1
Photo of the contributor
+ 1 contributors
On this page