Feedback from customers is one key to a successful business today. In this tutorial, we'll learn how to build a customer review and rating App using Strapi, a user-friendly and easy-to-integrate content management system(CMS) that simplifies the content management process, and solid.js, a reactive UI library.
We’ll go through the steps of setting up Strapi CMS backend, creating a content type and also integrating with solid.js to display the data.
Before we begin, ensure that you have the following:
To kick-start the building process, you need to have Strapi installed on your local machine. If you have already, you can skip this part to where we start building. For the new Strapiers 😀 navigate to the folder you want your project installed in the terminal and run this command:
npx create-strapi-app@latest my-project
Replace my-project
with the actual project name you intend on using.
This command will create a new project and install Strapi CMS on your local machine. Once that is installed, you can now start your Strapi application by running the command:
npm run develop
After running that command, copy the URL from your terminal and paste it into any browser of your choice. You’ll need to sign up to access your Strapi dashboard. This shouldn’t take long as the process is seemingly fast and easy. After you’re done signing up, you should have a dashboard like this:
Do you love the dark feature? Me too 🙃. You can enable dark mode for yours too. Simply go to your profile settings. Scroll down to Experience
and select dark mode in the user interface section.
Now, let’s create some content!
Collection type are a form of content type that is used to manage a list of content which are similar. For this tutorial, you’ll be creating a collection-type content. Navigate to Content-Type Builder on the side navigation bar of your dashboard, click on "Create new collection type".
Create a new collection type by giving it a display name. Ensure it’s singular, not plural, as Strapi automatically pluralizes it. I’ll be naming mine customer-review
, go ahead and give yours a unique name.
Click on continue. This will take us to the next step, which is selecting the appropriate fields for your collection type. For the customer-review
collection type, you need to create three fields representing:
reviewers_name
): The name of the customer or reviewer that is rating the product. This will be a field of type text,
which will be a short text. I’ll name mine reviewers_name.
Click on "add another field" to add the next field.
reviewers_rating
): This will be the actual rating the customer gives to the product. It could be a 5-star rating or a one-star rating. This field will be a number, so go ahead and click the number field, give it the name reviewers_rating
, and then choose a number format. I’ll go for integer
. Click on "add another field" to add the next field.
the_review
): This will contain the review and feedback of the product by the customer or reviewer. I’ll name mine the_review
. This field will also be a text, but a long text
.Click on "finish" and then save the collection type. Your collection type should look like this:
Once that is done, head over to Settings > USERS & PERMISSIONS PLUGIN > Roles, and click on Public. Then scroll down to Permissions, click on customer-review
, select all to authorize the application's activity, and click Save.
You need to add some content to the customer-review
collection type that was created. This will help show it works when we integrate it into Solid.js. To do this, head over to Content Manager and navigate to customer-review
collection type. Click on "create new entry". Fill in and then save it. You can populate with as much content as you want.
So, we’re done with setting up Strapi and our content. Let’s move on to setting up and building the frontend.
For our frontend, we’ll make use of Solid.js. Let’s introduce Solid.js a bit before moving forward.
Created by Ryan Carniato and open-sourced in 2018. Solid.js is a JavaScript framework for building user interfaces. It is a reactive framework that leverages fine-grained reactivity to efficiently update the user interface when data changes.
Solid.js possesses some features, including:
You can check out their documentation to learn more about Solid.js. Let’s proceed to build our frontend.
To install Solid.js, navigate once again to the project folder through the terminal. Run the following command:
npx degit solidjs/templates/js frontend
> cd frontend
> npm i # or yarn or pnpm
> npm run dev
Once you’ve run this command, you should have your solid.js development server start up. Click or copy the URL in the terminal to your browser.
We’ll need to install solid-bootstrap
to utilize some of its features like the form,
card,
button
, and others. Run this command:
npm install solid-bootstrap
Since our focus is not on styling, you can choose to style your project yourself. However, I included minimal CSS styling, which I used for this project here. Copy and paste it in the index.css
file which you can find in the src
folder.
Now we’ve got everything set up to start building.
We’ll have two components in addition to the App
component: the ReviewForm
component and the ReviewCard
component. The ReviewForm
component will contain the form for which the reviews and ratings are imputed. The ReviewCard
component will contain the reviews and ratings given by the customer and saved in the Strapi backend. Of course, the App.jsx
component brings them all together.
Inside the src
folder, create a subfolder called components
. This will house the two components. Create a file inside the subfolder called ReviewCard.jsx
and the following code:
1 // ReviewCard.jsx
2import { createSignal } from "solid-js";
3import { Card, Button } from "solid-bootstrap";
4
5function ReviewCard({ review, onDelete }) {
6 return (
7 <Card className="card">
8 <Card.Body>
9 <p>{review.attributes.reviewers_name}</p>
10 {[...Array(review.attributes.reviewers_rating)].map((_, index) => (
11 <span key={index} className="text-warning">
12 ★
13 </span>
14 ))}
15 <p>{review.attributes.the_review}</p>
16 <Button variant="danger" onClick={() => onDelete(review.id)}>
17 Delete
18 </Button>
19 </Card.Body>
20 </Card>
21 );
22}
23
24export default ReviewCard;
The code above creates a component called ReviewCard
,which displays information about the reviews.
Next, still inside the subfolder called components
, create another file called ReviewForm
. Add the following code:
1// ReviewForm.jsx
2import { createSignal } from "solid-js";
3import { Form, Button } from "solid-bootstrap";
4
5function ReviewForm({ fetchReviews }) {
6 const [clicked, setClicked] = createSignal(false);
7 const [stars, setStars] = createSignal(0);
8 const [hoveredStars, setHoveredStars] = createSignal(0);
9 const [name, setName] = createSignal("");
10 const [review, setReview] = createSignal("");
11
12 const onMouseOver = (rating) => {
13 if (clicked()) return;
14 setHoveredStars(rating);
15 };
16
17 const onMouseOut = () => {
18 if (clicked()) return;
19 setHoveredStars(0);
20 };
21
22 const onClick = (rating) => {
23 setClicked(!clicked());
24 setStars(rating);
25 };
26
27 const submitReview = async (e) => {
28 e.preventDefault();
29 const reviewData = {
30 data: {
31 reviewers_name: name(),
32 reviewers_rating: stars(),
33 the_review: review(),
34 },
35 };
36
37 try {
38 const response = await fetch(
39 "http://localhost:1337/api/customer-reviews/",
40 {
41 method: "POST",
42 headers: {
43 "Content-Type": "application/json",
44 },
45 body: JSON.stringify(reviewData),
46 },
47 );
48
49 if (!response.ok) {
50 console.error("Response status:", response.status);
51 console.error("Response text:", await response.text());
52 throw new Error("Failed to submit review");
53 }
54
55 // Re-fetch reviews to include the new submission
56 fetchReviews();
57
58 // Reset form
59 setName("");
60 setReview("");
61 setStars(0);
62 setHoveredStars(0);
63 setClicked(false);
64 } catch (error) {
65 console.error("Error submitting review:", error);
66 }
67 };
68
69 return (
70 <div className="form-container">
71 <Form onSubmit={submitReview}>
72 <Form.Group className="star-rating">
73 <Form.Label>Your Rating:</Form.Label>
74 {[...Array(5)].map((_, i) => (
75 <span
76 key={i}
77 className={`star ${
78 i < (hoveredStars() || stars()) ? "selected" : ""
79 }`}
80 onMouseOver={() => onMouseOver(i + 1)}
81 onMouseOut={onMouseOut}
82 onClick={() => onClick(i + 1)}
83 >
84 ★
85 </span>
86 ))}
87 </Form.Group>
88 <div className="input-group">
89 <Form.Label for="name">Name:</Form.Label>
90 <Form.Control
91 id="name"
92 type="text"
93 value={name()}
94 onInput={(e) => setName(e.currentTarget.value)}
95 className="form-input"
96 />
97 </div>
98 <div className="input-group">
99 <Form.Label for="review">Review:</Form.Label>
100 <Form.Control
101 id="review"
102 as="textarea"
103 rows={3}
104 value={review()}
105 onInput={(e) => setReview(e.currentTarget.value)}
106 className="form-input"
107 />
108 </div>
109 <Button variant="success" type="submit" disabled={review() === ""}>
110 Submit
111 </Button>
112 </Form>
113 </div>
114 );
115}
116
117export default ReviewForm;
The code above defines the ReviewForm
component, which will be used alongside the ReveiwCard
component. It includes a form for users to submit reviews, which involves entering a star rating and their names, as well as writing a review. The form handles this submission by sending the data to the Strapi backend through the API endpoint and resets itself after successful submission.
App.jsx
You need to modify App.jsx
to most of the logic, which includes fetching the reviews from the Strapi backend, displaying them, and deleting them.
Navigate to your App.jsx
file and modify it to this:
1 // App.jsx
2import { createSignal, createEffect } from "solid-js";
3import { Container, Col, Row } from "solid-bootstrap";
4import ReviewCard from "./components/ReviewCard";
5import ReviewForm from "./components/ReviewForm"; // Import the ReviewForm component
6import "./index.css";
7
8function App() {
9 const [reviews, setReviews] = createSignal([]);
10
11 // Fetch reviews from Strapi backend
12 const fetchReviews = () => {
13 fetch("http://localhost:1337/api/customer-reviews/")
14 .then((response) => response.json())
15 .then((data) => {
16 if (Array.isArray(data.data)) {
17 setReviews(data.data);
18 } else {
19 console.error("Expected an array of reviews, but received:", data);
20 setReviews([]);
21 }
22 })
23 .catch((error) => console.error("Error fetching reviews:", error));
24 };
25
26 createEffect(() => {
27 fetchReviews();
28 });
29
30 const deleteReview = async (id) => {
31 try {
32 const response = await fetch(
33 `http://localhost:1337/api/customer-reviews/${id}`,
34 {
35 method: "DELETE",
36 },
37 );
38
39 if (!response.ok) {
40 throw new Error("Failed to delete review");
41 }
42
43 // Re-fetch reviews to reflect the deletion
44 fetchReviews();
45 } catch (error) {
46 console.error("Error deleting review:", error);
47 }
48 };
49
50 return (
51 <>
52 <Container fluid className="App text-light text-center">
53 <Col md={{ span: 6, offset: 3 }}>
54 <Row className="mt-5">
55 <Col>
56 <ReviewForm fetchReviews={fetchReviews} />
57 </Col>
58 </Row>
59 <Row className="mt-5">
60 <Col>
61 <div className="cards-container">
62 {Array.isArray(reviews()) &&
63 reviews().map((r, rIndex) => (
64 <ReviewCard
65 key={rIndex}
66 review={r}
67 onDelete={deleteReview}
68 />
69 ))}
70 </div>
71 </Col>
72 </Row>
73 </Col>
74 </Container>
75 </>
76 );
77}
78
79export default App;
Here is a breakdown of what the code above does:
fetchReview
function fetches the reviews from the Strapi backend using the fetch
API. It sends a GET
request to the Strapi endpoint and updates the reviews state with the fetched data. reviews
state to display individual reviews. It checks if the reviews
state holds reviews in the form of an array. If it does, it loops through each review object and renders these reviews inside the ReviewCard
component, which is responsible for displaying them.DeleteReview
function to delete reviews. It sends a DELETE
request to the Strapi backend with the reviewers ID
. If the deletion is successful, it re-fetches the reviews to reflect the deletion.All set now, you can check the result in the browser.
Congratulations! You just built a customer review application in Solid.js utilizing Strapi as the backend. Here is the link to the full code on GitHub. Don't forget to give me a star.
In this tutorial, we looked at how to build a customer review application with Solid.Js alongside Strapi as the backend. We went through setting up Strapi for new users; we then proceeded to build our frontend using Solid.js and integrate it with Strapi to display, add, and delete reviews.
Strapi's uses for content purposes are endless, and we'll continue to explore them. Please share if you found this helpful!
I'm a web developer and writer. I love to share my experiences and things I've learned through writing.