How to Build a Review App with Hygraph and React

The ability to easily create, manage, and deliver content across multiple channels and devices is no longer a nice-to-have but a must-have for businesses looking to stay ahead of the curve. This is where Hygraph comes in – a powerful headless CMS revolutionizing how we think about content management.

In this article, I’ll take a look at what headless content management systems are and what makes Hygraph a game-changer.

What is a Headless CMS

A Headless CMS separates content storage (the “body”) from presentation (the “head”); instead of providing a pre-built frontend presentation layer or templating engine, a headless CMS offers a content repository and an API for content management. This separation increases flexibility and customizability by allowing the material to be reused and remixed across multiple platforms e.g. web, mobile, and digital media.

The primary benefit of a headless CMS is its flexibility.

  • It enables faster editing experiences and the capacity to manage material across many channels.
  • It allows developers to use their preferred frontend tooling and facilitates scaling. 

What is Hygraph CMS?

Hygraph CMS

Hygraph is a headless CMS that provides a unified tech stack for frontend and back-end development. It allows developers to manage and publish content and offers a rich set of features for content management, such as batch editing, commenting scheduled publishing, and content stages and workflows. 

One of the key features of Hygraph is its ability to create content from other systems and then deliver it to multiple destinations simultaneously. This comes in handy for businesses that operate on multiple platforms and need to ensure consistent content across all channels.

Hygraph’s generated APIs offer endless flexibility for fetching or writing content with precise content selection, filtering, ordering, pagination, union types, and mutations. This makes it ideal for developers who want to build highly customized applications.


How Does it Stand Out from Others?

What sets Hygraph apart from other CMS options is its focus on content flexibility.

Unlike traditional CMS platforms that are designed to manage static pages and posts, Hygraph is built to handle complex, data-driven content structures that can be easily adapted and reused across different channels and devices. This means that businesses can use Hygraph to create once and publish everywhere without having to worry about duplicating efforts or managing multiple versions of the same content.

Features of Hygraph

Hygraph offers a range of features designed to streamline the content management process. Some of these features include:

  1. Secure: Hygraph is SOC 2 Type 2 and GDPR compliant, ensuring the security and compliance of your data. 
  2. GraphQL: Hygraph leverages the power of GraphQL, to simplify the process of retrieving and modifying content for developers.
  3. Headless CMS: Hygraph decouples the content from the presentation layer, allowing you to use it with any frontend framework or technology.
  4. API-first: Hygraph is an API-first platform that lets you build your frontend apps without worrying about the underlying data model.
  5. Hygraph offers various team-friendly features, such as role-based access management and many more.
  6. Components: Hygraph enables you to build reusable components that may be used on multiple pages and projects.
  7. Schemas: Hygraph lets you define your content schema, specifying the structure and types of content that can be stored in Hygraph.

Let’s Build the Review App

Prerequisites

To proceed with this tutorial, you’ll need the following prerequisites:

  • An Hygraph account. 
  • A code editor or IDE of your choice (e.g., Visual Studio Code, IntelliJ IDEA, Sublime Text)
  • An understanding of React 
  • A basic understanding of GraphQL

Setting up the Environment

  • Login to your Hygraph account and proceed to create a new project.
  • Assign a name and description, and select the data storage region you prefer.
Hygraph CMS-React

Once we’re done with that, we’ll be redirected to this page:

Hygraph CMS-Dashboard

On our dashboard’s left side menu, click schema to create a model.

For this tutorial, our content type would be named “Review.” Include the fields needed.

Hygraph-CMS-Review-Schema


In the image above, the following fields were included:

  • Name(single text):  This will be the name of the one making the reviews.single text
  • Rating(int): This will be the rating given by the rating i.e., a rating of 4 for a product
  • Description(multi-line text) – This will represent what the customer has to say.

Let’s move on to add content.


Populating the Review

However, we’ll be creating the content from the front end into Hygraph.

Let’s add some content first.

To create the content, click on Content, and afterwards, click on “Add entry.” and then fill in some data. once that is done, click the save and publish button. You can create a few more if you prefer.

Hygraph-CMS-Content-Login-Dashboard

Hygragh provides a GraphQL API for viewing and testing created GraphQL queries. Click on the API Playground area. 


Roles and Permission

Roles and permissions are used to control access to the content and features within the CMS.

Before we can query our data within the React App, we must first obtain and configure our API endpoint and permissions to access or query any content published.

Go to Project Settings > Permanent Auth Tokens > Add Token, enter the name of your token, and then select Add & Configure Permission. Then, select the Add Permission button to add permissions. This allows anyone to make a public API call without requiring authentication.

Hygraph-CMS-Roles-Add-Permission

Creating the Frontend

To do this, we’ll create a fresh React application using the command line.

Open your terminal and enter the following command to generate a new React app.

I’ll be installing React using Vite.

Create your project and select React as the framework.

$ npm create vite@latest

npx: installed 1 in 0.969s

Project name:review-app

Select a framework:React

Select a variant:JavaScript

We need to install the dependencies needed for this project, which are graph-request and Bootstrap

We’ll make use of Bootstrap for our UI. To install, run this command:

npm install graphql-request react-bootstrap

Also, import the cd to index.html:

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" 
      rel="stylesheet" 
      integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" 
      crossorigin="anonymous">

And lastly, in the index.html, give the body tag a class of bg-dark. 

Let’s begin by importing the necessary dependencies, which are bootstrap and local styling. Inside our app.jsx paste this:

// src/App.js

import React, { useState } from "react";
import { Container, Col, Row, Button, Form, Card } from "react-bootstrap";
import "./app.css";

The next thing is to set up the state variables that will help us manage user interactions and input. Inside the function app() in the app.jsx, paste this:

// State Management

const [clicked, setClicked] = useState(false);
const [stars, setStars] = useState(0);
const [hoveredStars, setHoveredStars] = useState(0);
const [name, setName] = useState("");
const [review, setReview] = useState("");
const [reviews, setReviews] = useState([]);

In the code above, we utilize the useState hook to initialize various state variables. These include clicked to track if a star has been clicked, stars for the selected rating, and others for user input and review data.


Handle User Interactions

We need to  implement functions to handle user interactions, such as hovering over stars and clicking to select a rating:

// User Interaction Handling
const onMouseOver = (rating) => {
  if (clicked) return;
  setHoveredStars(rating);
};

const onMouseOut = () => {
  if (clicked) return;
  setHoveredStars(0);
};

const onClick = (rating) => {
  setClicked(!clicked);
  setStars(rating);
};

The onMouseOver, onMouseOut, and onClick functions will manage the visual feedback of star ratings when the user interacts with them.

Finally, we render the user interface. We’ll display a form consisting of 5 stars for rating,  form fields for name and review(description), and also a button containing the submitReview function processes for submitting reviews.

<Container fluid className="App text-light text-center">
  <Col md={{ span: 6, offset: 3 }}>

    {/* Star Rating Section */}
    <Row className="mt-5">
      <Col>
        {[...Array(5)].map((_, i) => (
          <span
            key={i}
            className={`star ${i < (hoveredStars || stars) ? "selected" : ""}`}
            onMouseOver={() => onMouseOver(i + 1)}
            onMouseOut={onMouseOut}
            onClick={() => onClick(i + 1)}
          >

          </span>
        ))}
      </Col>
    </Row>

    {/* User Input Section */}
    <Row className="mt-5">
      <Col>
        <Form.Group>
          <Form.Label>Name</Form.Label>
          <Form.Control
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </Form.Group>
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        <Form.Group>
          <Form.Label>Review</Form.Label>
          <Form.Control
            as="textarea"
            rows={3}
            value={review}
            onChange={(e) => setReview(e.target.value)}
          />
        </Form.Group>
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        <Button
          variant="success"
          onClick={(e) => submitReview(e)}
          disabled={review === ""}
        >
          Submit
        </Button>
      </Col>
    </Row>

  </Col>
</Container>

Let’s add some basic styling to it. Inside the app.css, add this:

.card {
  padding: 2em;
}

.read-the-docs {
  color: #888;
}

.star {
  cursor: pointer;
  color: #e4e5e9; /* Default color */
}

.star.selected,
.star:hover {
  color: #ffc107; /* Gold color */
}

Save run the the development server:

npm run dev

We should see something like this:

Review-App-React

Let’s integrate Hygraph so we can display, create, and delete reviews.


Integrating Hygraph

We’ll use the graphql-request library to send requests to the Hygraph API.

First, we initialize a new instance of GraphQLClient with the URL of our Hygraph API. 

import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient("YOUR API ENDPOINT");

replace the ‘YOUR API ENDPOINT” with your actual Hygragh endpoint

Let’s define GraphQL operations that facilitate interactions between the review app’s front-end and the GraphQL server.

These operations include creating a new review, publishing a review, retrieving existing reviews, and deleting reviews based on their respective functionalities. 

const CREATE_REVIEW = `
  mutation CreateReview($name: String!, $rating: Int!, $description: String!) {
    createReview(
      data: {
        name: $name,
        rating: $rating,
        description: $description
      }
    ) {
      id
      name
      rating
      description
    }
  }
`;

const PUBLISH_REVIEW = `
  mutation PublishReview($id: ID!) {
    publishReview(where: { id: $id }, to: PUBLISHED) {
      id
    }
  }
`;

const GET_REVIEWS = `
  query GetReviews {
    reviews {
      id
      name
      description
      createdAt
      rating
      updatedAt
    }
  }
`;

const DELETE_REVIEW = `
  mutation DeleteReview($id: ID!) {
    deleteReview(where: { id: $id }) {
      id
    }
  }
`;

We’ll use the useEffect hook to fetch the reviews when the component mounts. Inside

useEffect(() => {
  const fetchReviews = async () => {
    try {
      const data = await client.request(GET_REVIEWS);
      setReviews(data.reviews);
    } catch (error) {
      console.error("Error fetching reviews:", error);
    }
  };

  fetchReviews();
}, []);

We then create a submit review function that sends the CREATE_REVIEW and PUBLISH_REVIEW mutations to the Hygraph API.

const submitReview = async (e) => {
  e.preventDefault();

  const variables = { name: name, rating: stars, description: review };

  try {
    // First, create the review
    const createResponse = await client.request(CREATE_REVIEW, variables);

    // Then, publish the review
    const publishResponse = await client.request(PUBLISH_REVIEW, { id: createResponse.createReview.id });

    setName("");
    setReview("");
    setStars(0);
    setHoveredStars(0);
    setClicked(false);

    // Fetch reviews again to update the reviews list
    const data = await client.request(GET_REVIEWS);
    setReviews(data.reviews);
  } catch (error) {
    console.error("Error creating or publishing review:", error);
  }
};

We then create a deleteReview function that sends this mutation to the Hygraph API.

const deleteReview = async (id) => {
  try {
    // Send the DELETE_REVIEW mutation
    await client.request(DELETE_REVIEW, { id: id });

    // Fetch reviews again to update the reviews list
    const data = await client.request(GET_REVIEWS);
    setReviews(data.reviews);
  } catch (error) {
    console.error("Error deleting review:", error);
  }
};

Lastly, update the form to allow users to submit new reviews.

We also display it in the front-end from our Hygragh cms.

Update the container code to this.

<Container fluid className="App text-light text-center">
  <Col md={{ span: 6, offset: 3 }}>

    <Row className="mt-5">
      <Col>
        {[...Array(5)].map((_, i) => (
          <span
            key={i}
            className={`star ${i < (hoveredStars || stars) ? "selected" : ""}`}
            onMouseOver={() => onMouseOver(i + 1)}
            onMouseOut={onMouseOut}
            onClick={() => onClick(i + 1)}
          >

          </span>
        ))}
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        <Form.Group>
          <Form.Label>Name</Form.Label>
          <Form.Control
            type="text"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
        </Form.Group>
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        <Form.Group>
          <Form.Label>Review</Form.Label>
          <Form.Control
            as="textarea"
            rows={3}
            value={review}
            onChange={(e) => setReview(e.target.value)}
          />
        </Form.Group>
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        <Button
          variant="success"
          onClick={(e) => submitReview(e)}
          disabled={review === ""}
        >
          Submit
        </Button>
      </Col>
    </Row>

    <Row className="mt-5">
      <Col>
        {reviews.map((r, rIndex) => (
          <Card key={rIndex} className="mt-3 mb-3 text-dark">
            <Card.Body>
              <p>{r.name}</p>
              {[...Array(r.rating)].map((_, sIndex) => (
                <span key={sIndex} className="text-warning">

                </span>
              ))}
              <p>{r.description}</p>
              <Button variant="danger" onClick={() => deleteReview(r.id)}>
                Delete
              </Button>
            </Card.Body>
          </Card>
        ))}
      </Col>
    </Row>

  </Col>
</Container>
const submitReview = async (e) => {
  e.preventDefault();

  const variables = { name: name, rating: stars, description: review };

  try {
    // First, create the review
    const createResponse = await client.request(CREATE_REVIEW, variables);

    // Then, publish the review
    const publishResponse = await client.request(PUBLISH_REVIEW, { id: createResponse.createReview.id });

    setName("");
    setReview("");
    setStars(0);
    setHoveredStars(0);
    setClicked(false);

    // Fetch reviews again to update the reviews list
    const data = await client.request(GET_REVIEWS);
    setReviews(data.reviews);
  } catch (error) {
    console.error("Error creating or publishing review:", error);
  }
};

In the updated code, we added a new section for displaying the existing reviews below the form for submitting new reviews.

This section maps through the reviews array and creates a Card for each review. Each Card displays the reviewer’s name, the star rating, the review, and a delete button. When the delete button is clicked, the deleteReview function is triggered with the id of the review to be deleted.

Let’s check the result in our browser.

And that’s a wrap.


Conclusion

In this article, I’ve talk about on building a review app using React and Hygraph.

We started by setting up a basic React application and installing the necessary dependencies. We then created the front-end of our application using React Bootstrap, which included a form for submitting reviews and a list for displaying existing reviews. After that, we integrated Hygraph into our application, allowing us to interact with our database using GraphQL. 

Hygraph offers a comprehensive suite of tools and features that go beyond the traditional role of a content management system, empowering developers to create innovative, reliable, and secure digital solutions.

I hope this post is helpful please do share on your socials if you find it helpful.

If you want to read more about ReactJS then start reading some of recent articles.

Leave a Comment

Your email address will not be published. Required fields are marked *