Form validation with Yup
Tags: react, sanity, formik, yup
Published: 2021-04-21 22:49:35

## Introduction

The primary aim of this article is to show you how to manage and validate forms in React using Formik and Yup. You can see the form we will build [here](https://codesandbox.io/s/form-validation-with-formik-and-yup-8h8h3?file=/src/App.js). In addition to this, you will also learn how to set up and manage a CRM using Sanity as well as reading and writing data to a Sanity hosted CRM using GROQ. The code for the complete project is available on [Github](https://github.com/ybjozee/product-review-site) should you get stuck at any point.

Forms are a key medium of collecting data from people interacting with your application. This data could prove crucial in future activities such as sending notifications or even transferring money to saved accounts. As such it is important to reduce the possibility of invalid data being supplied to the barest minimum. The first step to doing that is via form validation.

Simply put, form validation is the process of ensuring that appropriate data is supplied in a form. Via this process, an application is not only able to ensure that required information is provided at all times, but that all data is provided in an appropriate format.

In React, there are many options for managing forms - Formik, Unform, React Hook Form, and Redux Form to name a few. However, [Formik](https://formik.org/docs/overview) has proven itself to be a popular choice as it combines efficiency, ease of use, and a small library footprint to provide a smooth developer experience. Another positive for using Formik is its seamless integration with Yup.

When it comes to validation in JavaScript, no library comes to mind faster than [Yup by JQuense](https://www.google.com/search?q=yup+github&oq=YUp+gi&aqs=chrome.0.0j69i57j0l4j69i60l2.7139j0j7&sourceid=chrome&ie=UTF-8#:~:text=jquense/yup,jquense%20%E2%80%BA%20yup). With Yup, the developer can define a schema (or structure) of the expected data specifying its data type and whether it is required or not. With Yup, you are also able to determine the structure of data in the yup schema including input length, or even validate the supplied data against a regular expression (regex validation).

In this article, we will build a small application that allows customers to leave product reviews for an e-commerce website. We will build the web application with React while the content (and back end) will be hosted on [Sanity](https://www.sanity.io/). Communication between the web application and back end will be via [Graph-Relational Object Queries (GROQ)](https://www.sanity.io/docs/groq).

## Prerequisites

To keep up with the concepts that will be introduced in this article, you will need a fair grasp of ES6. You will also need to have an understanding of some basic React concepts like hooks and functional components. 

 
You will also need to have the following installed on your workstation:

1. [Node Package Manager (NPM)](https://www.npmjs.com/get-npm)
2. [Sanity CLI](https://www.sanity.io/docs/getting-started-with-sanity-cli)
3. A code editor of your choice

## Form validation

Create a new folder called `product-review-site` to hold the code for the front-end and back-end.

```bash
mkdir product-review-site

cd product-review-site
```

In the `product-review-site` folder create a new React application named `front-end`. 

```bash
npx create-react-app front-end
```

In the `product-review-site` folder create a new React application named `front-end`. 

```bash
npx create-react-app front-end
```

Create a new directory called `components` in the `front-end/src` directory.

```bash
mkdir front-end/src/components
```

For this tutorial, we will use [Bulma CSS](https://bulma.io/) to style our application. Use the following command to add Bulma CSS to your application

```bash
npm install bulma
```

To import the Bulma CSS into the project, create a file named `style.css`  in the `src` directory and add the following:

```css
@import 'bulma/css/bulma.css'
```

> You can delete the `App.css` and `index.css` files we’ll be relying on Bulma for our styling.

Import  `style.css` in the `src/index.js` file as follows:

```jsx
import './style.css';
```

In this tutorial, we will use Formik to manage our forms while using Yup for validation. Install both packages using the command below:

```bash
npm install formik yup
```

## Building the reviews component

In the `components` directory, create a file called `ReviewForm.jsx`. This component will be responsible for managing the form displayed for a customer to review a product. For now, the form will return a [React Fragment](https://reactjs.org/docs/fragments.html).

```jsx
import React from "react";

const ProductReviewForm = () => <></>;

export default ProductReviewForm;
```

A product review will have the following inputs:

1. The product being reviewed. This field is required and must be from the list of products currently available.
2. The name of the customer. This field is also required.
3. The customer’s email address. This field is required. Additionally, the email address must be valid.
4. The title of the review. This field is essentially the review in less than 10 words. This is required.
5. A more detailed review. This field is required.
6. A rating of the product with 1 as the lowest and 10 as the highest. This field is required.
7. The date the review was given. This will be generated automatically.
8. Whether the customer would recommend the product to someone else. This is optional and by default should be false.

We will use a [Formik](https://formik.org/docs/api/formik) component as the root component of our ReviewForm. This component will have three props - a set of initial values, a validation schema, and a callback function to be triggered when the form is submitted.

```jsx
import React from "react";
import * as Yup from "yup";

const ProductReviewForm = () => {
  const products = ["Product 1", "Product 2", "Product 3", "Product 4"];

  const validationSchema = Yup.object({
    product: Yup.string().required("Please select a product").oneOf(products),
    name: Yup.string().required(),
    email: Yup.string().email().required(),
    title: Yup.string().required(),
    review: Yup.string().required(),
    rating: Yup.number().min(1).max(10).required(),
    date: Yup.date().default(() => new Date()),
    wouldRecommend: Yup.boolean().default(false),
  });

  const initialValues = {
    name: "",
    email: "",
    title: "",
    review: "",
    rating: "",
    date: new Date(),
    wouldRecommend: false,
    product: "",
  };

  const onSubmit = (values) => {
    alert(JSON.stringify(values, null, 2));
  };

  return <></>;
};

export default ProductReviewForm;
```

> We are using a dummy array of products for now. We will update the `products` array and `onSubmit` callback after setting up the API.

In addition to the `Formik` component, Formik provides the [Form](https://formik.org/docs/api/form) , [Field](https://formik.org/docs/api/field) and [ErrorMessage](https://formik.org/docs/api/errormessage) components which all work together to handle form state, events, validation (based on the object schema provided by Yup) and display of validation errors. This allows us to focus on the structure of the form and the submission process.

Before returning the JSX for the `ProductReviewForm`, we'll declare a constant called `productOptions` which is an array of options for the dropdown of products. We'll also declare a function called `renderError` which will be passed as a prop to the `ErrorMessage` component and used to display a properly styled error message. 

```jsx
//other import statements
import { Formik, Form, Field, ErrorMessage } from "formik";

const ProductReviewForm = () => {
  //declaration of products
  //declaration of validationSchema
  //declaration of initialValues
  //declaration of onSubmit callback

  const productOptions = products.map((product, key) => (
    <option value={product} key={key}>
      {product}
    </option>
  ));

  const renderError = (message) => <p className="help is-danger">{message}</p>;

  return (
    <Formik
      initialValues={initialValues}
      validationSchema={validationSchema}
      onSubmit={async (values, { resetForm }) => {
        await onSubmit(values);
        resetForm();
      }}
    >
      <Form>
        <div
          className="container"
          style={{
            width: "60%",
          }}
        >
          <div className="field">
            <label className="label" htmlFor="name">
              Full name
            </label>
            <div className="control">
              <Field
                name="name"
                type="text"
                className="input"
                placeholder="Full name"
              />
              <ErrorMessage name="name" render={renderError} />
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="email">
              Email address
            </label>
            <div className="control">
              <Field
                name="email"
                type="text"
                className="input"
                placeholder="Email address"
              />
              <ErrorMessage name="email" render={renderError} />
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="product">
              Product
            </label>
            <div className="control">
              <Field name="product" as="select" className="select is-fullwidth">
                <option value={""}>Select a product</option>
                {productOptions}
              </Field>
              <ErrorMessage name="product" render={renderError} />
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="title">
              Title
            </label>
            <div className="control">
              <Field
                name="title"
                type="text"
                className="input"
                placeholder="Title"
              />
              <ErrorMessage name="title" render={renderError} />
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="review">
              Review
            </label>
            <div className="control">
              <Field
                name="review"
                as="textarea"
                className="textarea"
                placeholder="Review"
              />
              <ErrorMessage name="review" render={renderError} />
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="rating">
              Rating
            </label>
            <div className="control">
              <Field
                name="rating"
                type="number"
                className="input"
                placeholder="Rating"
              />
              <ErrorMessage name="rating" render={renderError} />
            </div>
          </div>
          <div className="field">
            <div className="control">
              <label className="checkbox label" htmlFor="wouldRecommend">
                <Field
                  name="wouldRecommend"
                  type="checkbox"
                  className="checkbox"
                />
                Would recommend
              </label>
            </div>
          </div>
          <button type="submit" className="button is-primary">
            Submit
          </button>
        </div>
      </Form>
    </Formik>
  );
};
export default ProductReviewForm;
```

To see the form in action, edit the `src/App.js` file to render the `ProductReviewForm`.

```jsx
import ProductReviewForm from "./components/ReviewForm";

const App = () => <ProductReviewForm />;

export default App;
```

Run your application. By default, your application will be available on [http://localhost:3000/](http://localhost:3000/). 

```bash
npm start
```

Your application should look like this at the moment:

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615336079760_Screenshot+2021-03-10+at+01.27.48.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615336079760_Screenshot+2021-03-10+at+01.27.48.png)

Click the `submit` button and see the error messages pop up.

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1619045137799_Screenshot_2021-03-13_at_01.05.58.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1619045137799_Screenshot_2021-03-13_at_01.05.58.png)

When filled properly and submitted, we see the following:

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615334080981_Screenshot+2021-03-10+at+00.43.47.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615334080981_Screenshot+2021-03-10+at+00.43.47.png)

## Creating the API

We will use Sanity to build our API. Initialize a new Sanity project named `back-end` in the `product-review-site` folder.

```bash
cd product-review-site 

sanity init
```

You will be  prompted to provide some information. Proceed as follows:

1. Select the `Create new project` option
2. Name the project `back-end`
3. Use the default dataset configuration (press `Y`)
4. Select the project output path (by default it would be the `product-review-site` folder)
5. Select the e-commerce (schema + sample data) option. Using the arrow keys, navigate to that option (it turns blue when selected).
6. Upload a sampling of products to go with your e-commerce schema (press `Y`)

The Sanity CLI will bootstrap a project from the e-commerce template, link the needed dependencies and populate the backend with sample data (in our use case, this will be products that we can make reviews for).

Before we run our backend, let us define a schema for our product reviews. In the `back-end/schemas` directory, create a file named `productReview.js`. In it, declare the schema for a product review.

```jsx
export default {

  name: "productReview",
  title: "Product Review",
  type: "document",
  fields: [
    {
      name: "product",
      type: "reference",
      to: { type: "product" },
    },
    {
      name: "title",
      type: "string",
      description: "Your review in less than 10 words",
    },
    {
      name: "name",
      type: "string",
      title:"Full name",
    },
    {
      name: "email",
      type: "string",
      title:"Email address"
    },
    {
      name: "rating",
      type: "number",
      description: "Rate the product out of 10",
    },
    {
      name: "date",
      type: "datetime",
    },
    {
      name: "review",
      type: "text",
      description: "Your review in as many words as you can come up with",
    },
    {
      name: "wouldRecommend",
      title: "Would recommend to a friend",
      type: "boolean",
    },
  ],
};
```

Next, open the `schema.js` file (in the `back-end/schemas` directory) and include the newly created schema:

```jsx
//other import statements
import productReview from './productReview';

export default createSchema({
  name: 'default',
  types: schemaTypes.concat([
// other predefined schemas
    productReview,
  ]),
});
```

Run your studio. By default, the studio runs at [http://localhost:3333](http://localhost:3333/).

```bash
sanity start
```

Your studio should be as shown below:

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615358001282_Screenshot+2021-03-10+at+07.33.11.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615358001282_Screenshot+2021-03-10+at+07.33.11.png)

Using the studio we can add new products and reviews for products by clicking the compose icon.

## Enable CORS on the API

By default, the only host that can connect to the project API is the sanity studio ([http://localhost:3333](http://localhost:3333/)). Before we can make requests to the API, we need to add the host for our React application ([http://localhost:3000/](http://localhost:3000/)) to the permitted origins. To do this open, your [Sanity Content Studio](https://manage.sanity.io/projects). This will show you all the Sanity projects you have initialized. Select the project we are working on (`back-end`) and click on the `Settings` tab. Click on the `API` menu option.

In the  CORS Origins section, click the `Add new origin` button.  In the form that is displayed, type `http://localhost:3000` as the origin and enable the `Allow credentials` option. We will need the credentials so that we can make authenticated requests to our API when posting product reviews. Click the `Add new origin` button to save the changes made.

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615424888752_Screenshot+2021-03-11+at+01.57.14.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615424888752_Screenshot+2021-03-11+at+01.57.14.png)

Because we will be making `write` requests to the API, we need a token that is authenticated by the API. To create one, click `Add new token` in the `Tokens` section. As a label, you can provide anything memorable. This time we’ll use `Front-End` as the label. Make sure you check the `write` option in the Rights section. Click `Add new token` to view the newly generated token. 

> **MAKE SURE YOU COPY THE TOKEN BEFORE CLOSING THE MODAL AS IT WILL ONLY BE DISPLAYED ONCE.**

One last thing to note before we return our attention to the review form is the project ID. We will need this when we link the review form to the backend. In the root directory of the backend project, open the  `sanity.json` file and look for the `api` node. This node will contain the project ID as well as the targeted dataset.

With these in hand, we can link our front-end to the API. This will allow us to get the list of products as well as save and retrieve product reviews. 

## Linking the React application to Sanity

To make our product review form fully functional, we need to pull the list of products from the backend- so that the customer can select the product to be reviewed. We also need to post the review to the API when the user submits the form (with valid input). We will use the [Sanity Client](https://www.sanity.io/docs/js-client) package to handle interactions with the API.

Add the `@sanity/client` dependency to your **front-end project**.

```bash
npm install @sanity/client
```

In the `src` directory, create a file called `client.js`. In it, add the following:

```jsx
import sanityClient from "@sanity/client";
export default sanityClient({
  projectId: "INSERT_YOUR_PROJECT_ID_HERE",
  dataset: "production",
  // authenticated requests can't be cached so we have to set useCdn to false
  useCdn: false, 
  token: "INSERT_YOUR_TOKEN_HERE"   
});
```

> The token is saved here for the sake of brevity. You should not do the same in applications that will be made available to the public. You can read more about securing tokens [here](https://www.sanity.io/help/js-client-browser-token).

Back in our `ProductReviewForm`, remove the `products` declaration. We will take advantage of the `useState` and `useEffect` React hooks to make a fetch request to the API and save the response to the `products` array.

```jsx
// other import statements
import sanityClient from "../client";
import { useEffect, useState } from "react";

const ProductReviewForm = () => {
 
  const [products, setProducts] = useState([]);
  
  useEffect(() => {
    sanityClient
      .fetch(`*[_type == "product"]{_id, title}`)
      .then((products) => {
        setProducts(products);
      })
      .catch(console.error);
  }, []);

// other declarations
// return statement
};

export default ProductReviewForm;
```

Using our configured Sanity client, we get the products by making a fetch request with a GROQ statement. The statement is broken down as follows:

1. `*`fetches all documents
2. `[_type == "product"]` filters all the documents and returns only the documents of the type `product`.
3. `{_id, title}` selects only the id and title from each document. We need the id to let the backend know which product the review is referencing. We need the title for the dropdown of product options.

We also need to update our validation schema. Previously, we were only validating to check that the selected product was in the array of products. Now that we have a list of product objects, we need to ensure that whatever value is provided to the schema matches the id of a product in our array of products. Replace the validationSchema declaration with the following:

```jsx
const validProductValues = products.map(({ _id }) => _id);

  const validationSchema = Yup.object({
    product: Yup.string()
      .required("Please select a product")
      .oneOf(validProductValues),
    name: Yup.string().required(),
    email: Yup.string().email().required(),
    title: Yup.string().required(),
    review: Yup.string().required(),
    rating: Yup.number().min(1).max(10).required(),
    date: Yup.date().default(() => new Date()),
    wouldRecommend: Yup.boolean().default(false),
  });
```

We also need to update the `productOptions` constant. Replace the `productOptions` declaration with the following:

```jsx
const productOptions = products.map(({ _id, title }, key) => (
      <option value={_id} key={key}>
        {title}
      </option>
    ));
```

To save the reviews on the back-end, we need to update the `onSubmit` function.

```jsx
const onSubmit = (values) => {
    const { product } = values;
    const request = { ...values, product: { _ref: product } };
    sanityClient.create(request).then(() => {
      alert(`Review submitted successfully`);
    });
  };
```

> We replace the `product` key in the request with a `_ref` value of the selected product id. This lets the backend know which product we’re referencing in the submitted review.

## Building the rest of the front-end

In addition to the review form we just created, let’s make it possible for customers to see all the available products and reviews. To do this we’ll build the following:

1. A component to view all products
2. A component to view all product reviews

## Image handling

Because we will also be loading product images on our pages, let’s add the [Sanity Image-URL](https://www.sanity.io/docs/image-url) dependency to our project:

```bash
npm install @sanity/image-url
```

In the `src` folder create a file named `imageHelper.js` and add the following code:

```jsx
import imageUrlBuilder from "@sanity/image-url";
import sanityClient from "./client";

const getImageUrl = (source) => {
  const builder = imageUrlBuilder(sanityClient);
  return builder.image(source);
};

export default getImageUrl;
```

In the components directory, create a file called `Products.jsx`. In it, add the following code.

```jsx
import React, { useEffect, useState } from "react";
import sanityClient from "../client";
import getImageUrl from '../imageHelper';

const Products = () => {

  const [products, setProducts] = useState(null);

  useEffect(() => {
    sanityClient
      .fetch(
        `*[_type == "product"]{
          title, 
          blurb,
          defaultProductVariant
      }`
      )
      .then((products) => {
        setProducts(products);
      })
      .catch(console.error);
  }, []);

  return (
    <div className="container">
      <div className="columns is-multiline">
        {products &&
          products.map((product, index) => (
            <div className="column is-3" key={index}>
              <div className="card">
                <div className="card-image">
                  <figure className="image is-4by3">
                    <img
                      src={getImageUrl(
                        product.defaultProductVariant.images[0]
                      ).url()}
                      alt={product.title}
                    />
                  </figure>
                </div>
                <div className="card-content">
                  <div className="media">
                    <div className="media-content">
                      <p className="title is-4">{product.title}</p>
                      <p className="subtitle is-6">
                        ${product.defaultProductVariant.price}
                      </p>
                    </div>
                  </div>
                  <div className="content">
                    {product.blurb.en}
                    <br />
                  </div>
                </div>
              </div>
            </div>
          ))}
      </div>
    </div>
  );
};

export default Products;
```

## Building the Reviews component

For the product reviews interface, we’ll use a master-detail layout as shown in the image below:

![https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615586117666_Screenshot+2021-03-12+at+22.54.51.png](https://paper-attachments.dropbox.com/s_4F35F7ECF4EB035FF3200BD31C718D1B2D5280AB874BC1444B7D6588FEE733C0_1615586117666_Screenshot+2021-03-12+at+22.54.51.png)

To do this we’ll need 3 components:

1. A component to display the list of reviews
2. A component to display the selected review
3. A component to display the list and details of the selected review side by side

In the components directory, create a new directory called reviews. In the reviews directory add the following files:

1. `ReviewCard.jsx`
2. `ReviewDetails.jsx`
3. `Reviews.jsx`

In the `ReviewCard.jsx` file add the following:

```jsx
import React from "react";
import getImageUrl from "../../imageHelper";

const ReviewCard = ({ review, index, onClick, isSelected }) => {

  const { rating, name, title, product } = review;
  
  return (
    <div
      className="card"
      key={index}
      onClick={onClick}
      style={{
        backgroundColor: isSelected ? "#e2e2e2" : "white",
        marginBottom: "5px",
      }}
    >
      <div className="card-content">
        <div className="columns">
          <div className="column is-narrow">
            <div className="media-left">
              <figure className="image is-96x96">
                <img
                  src={getImageUrl(
                    product.defaultProductVariant.images[0]
                  ).url()}
                  alt={product.title}
                />
              </figure>
            </div>
          </div>
          <div className="column">
            <div className="media-content">
              <p className="title is-4">{product.title}</p>
              <p className="subtitle is-5">{title}</p>
              <p className="is-6">Rating: {rating} out of 10</p>
              <p className="is-6">{name}</p>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default ReviewCard;
```

In the `ReviewDetails.jsx` file add the following:

```jsx
import React from "react";
import getImageUrl from "../../imageHelper";

const ReviewDetails = ({ selectedReview }) => {
  
  if (!selectedReview) {
    return <p className="title is-5">Click on a card to get started</p>;
  }

  const { product, name, rating, email, date, review } = selectedReview;

  return (
    <>
      <figure className="image is-16by9" style={{ marginBottom: "5%" }}>
        <img
          src={getImageUrl(product.defaultProductVariant.images[0]).url()}
          alt={product.title}
        />
      </figure>
      <p className="title is-5">{product.title}</p>
      <p className="subtitle">{product.blurb.en}</p>
      <p className="subtitle">
        {name} said "<b>{review}</b>"
      </p>
      <p className="is-6">Rating: {rating} out of 10</p>
      <p className="subtitle is-6">
        Review provided by <a href={`mailto:${email}`}>{name}</a> on{" "}
        {new Date(date).toUTCString()}
      </p>
    </>
  );
};

export default ReviewDetails;
```

In the `Reviews.jsx` file, add the following:

```jsx
import React, { useEffect, useState } from "react";
import sanityClient from "../../client";
import ReviewCard from "./ReviewCard";
import ReviewDetails from "./ReviewDetails";

const ProductReviews = () => {

  const [reviews, setReviews] = useState(null);
  const [selectedReview, setSelectedReview] = useState(null);
  const [selectedReviewIndex, setSelectedReviewIndex] = useState(null);

  useEffect(() => {
    sanityClient
      .fetch(
        `*[_type == "productReview"]{
        name, 
        email,
        title, 
        rating,
        date,
        review,
        wouldRecommend,
        product ->{
          title, 
          blurb,
          slug, 
          defaultProductVariant
        }
      }`
      )
      .then((reviews) => {
        setReviews(reviews);
      })
      .catch(console.error);
  }, []);

  const isSelected = (index) => selectedReviewIndex === index;

  return (
    <div className="container">
      <div className="columns">
        <div className="column is-narrow">
          {reviews &&
            reviews.map((review, index) => (
              <ReviewCard
                review={review}
                index={index}
                onClick={() => {
                  setSelectedReview(review);
                  setSelectedReviewIndex(index);
                }}
                isSelected={isSelected(index)}
              />
            ))}
        </div>
        <div className="column">
          <div className="box" style={{ minHeight: "85vh" }}>
            <ReviewDetails selectedReview={selectedReview}/>
          </div>
        </div>
      </div>
    </div>
  );
};

export default ProductReviews;
```

Notice the following snippet in the GROQ statement we use to fetch all the reviews:

```jsx
product -> {
		          title, 
		          blurb,
		          slug, 
		          defaultProductVariant
		        }
```

Because we save a reference to the product when creating a review, we can destructure the referenced item using the `→` operator and access the fields enclosed in the curly brackets.

## Navigation

For ease of navigation, we’ll create a simple menu to link all the components we created. We’ll use [React Router](https://reactrouter.com/web) to handle routing between components so let’s add that to our project

```bash
npm install react-router-dom
```

In the `components` folder, create a file called `Menu.jsx` and add the following to it:

```jsx
import React from "react";
import { Link } from "react-router-dom";

const Menu = () => (
    <div className="container" style={{ marginTop: "2%" }}>
      <nav className="navbar" role="navigation" aria-label="main navigation">
        <div className="navbar-brand">
          <Link className="button is-primary" to={"/"}>
            Home
          </Link>
        </div>
        <div className="navbar-menu">
          <div className="navbar-end">
            <Link className="navbar-item" to="/products">
              Products
            </Link>
            <div className="navbar-item has-dropdown is-hoverable">
              <a className="navbar-link">Reviews</a>
              <div className="navbar-dropdown">
                <Link className="navbar-item" to="/review">
                  Add review
                </Link>
                <Link className="navbar-item" to="/">
                  View all
                </Link>
              </div>
            </div>
          </div>
        </div>
      </nav>
    </div>
  );

export default Menu;
```

Update the `App.js` to include a `[BrowserRouter](https://reactrouter.com/web/api/BrowserRouter)`, our menu and a `[Switch](https://reactrouter.com/web/api/Switch)`

```jsx
import Products from "./components/Products";
import ProductReviewForm from "./components/ReviewForm";
import ProductReviews from "./components/reviews/Reviews";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Menu from "./components/Menu";

const App = () => (
  <Router>
    <Menu />
    <div
      style={{
        padding: "2%",
      }}
    >
      <Switch>
        <Route path="/" exact>
          <ProductReviews />
        </Route>
        <Route path="/products">
          <Products />
        </Route>
        <Route path="/review">
          <ProductReviewForm />
        </Route>
      </Switch>
    </div>
  </Router>
);

export default App;
```

With that, our product review website is ready for launch, Congratulations!!!

## Conclusion

Validating user input before saving to the database is a practice that will improve data integrity in web applications. In this article, we see the benefits of combining Formik and Yup for form validation. By taking advantage of Formik, we were able to manage and update the form state as the user triggered the `onChange` event. Also, by integrating Yup with Formik, we saved ourselves the burden of building a validator. This allows us to build industry standard forms more efficiently.