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.
Prerequisites
Before we begin, ensure that you have the following:
- Node installed (it is recommended that you have the latest version of Node installed or one that is compatible with installing Strapi on your local machine).
- Basic understanding of Javascript.
- Basic understanding of Solid.js.
- A code editor.
Setting up Strapi
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!
Create a Collection Type
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:
-
Reviewer's name (
reviewers_name
): The name of the customer or reviewer that is rating the product. This will be a field of typetext,
which will be a short text. I’ll name minereviewers_name.
Click on "add another field" to add the next field.
-
Reviewer's rating (
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 namereviewers_rating
, and then choose a number format. I’ll go forinteger
.
Click on "add another field" to add the next field.
-
The review (
the_review
): This will contain the review and feedback of the product by the customer or reviewer. I’ll name minethe_review
. This field will also be a text, but along text
.
Click on "finish" and then save the collection type. Your collection type should look like this:
Enable Public Access
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.
Populate the Collection
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.
Set up the frontend and Integrate with Strapi
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:
- Declarative Syntax
- Fine-Grained Reactivity
- JSX Support:
- Reactive Primitives
- Efficient Rendering
You can check out their documentation to learn more about Solid.js. Let’s proceed to build our frontend.
Install SolidJs
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.
Install Solid-bootstrap
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.
Create Components
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:
// ReviewCard.jsx
import { createSignal } from "solid-js";
import { Card, Button } from "solid-bootstrap";
function ReviewCard({ review, onDelete }) {
return (
<Card className="card">
<Card.Body>
<p>{review.attributes.reviewers_name}</p>
{[...Array(review.attributes.reviewers_rating)].map((_, index) => (
<span key={index} className="text-warning">
★
</span>
))}
<p>{review.attributes.the_review}</p>
<Button variant="danger" onClick={() => onDelete(review.id)}>
Delete
</Button>
</Card.Body>
</Card>
);
}
export 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:
// ReviewForm.jsx
import { createSignal } from "solid-js";
import { Form, Button } from "solid-bootstrap";
function ReviewForm({ fetchReviews }) {
const [clicked, setClicked] = createSignal(false);
const [stars, setStars] = createSignal(0);
const [hoveredStars, setHoveredStars] = createSignal(0);
const [name, setName] = createSignal("");
const [review, setReview] = createSignal("");
const onMouseOver = (rating) => {
if (clicked()) return;
setHoveredStars(rating);
};
const onMouseOut = () => {
if (clicked()) return;
setHoveredStars(0);
};
const onClick = (rating) => {
setClicked(!clicked());
setStars(rating);
};
const submitReview = async (e) => {
e.preventDefault();
const reviewData = {
data: {
reviewers_name: name(),
reviewers_rating: stars(),
the_review: review(),
},
};
try {
const response = await fetch(
"http://localhost:1337/api/customer-reviews/",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(reviewData),
},
);
if (!response.ok) {
console.error("Response status:", response.status);
console.error("Response text:", await response.text());
throw new Error("Failed to submit review");
}
// Re-fetch reviews to include the new submission
fetchReviews();
// Reset form
setName("");
setReview("");
setStars(0);
setHoveredStars(0);
setClicked(false);
} catch (error) {
console.error("Error submitting review:", error);
}
};
return (
<div className="form-container">
<Form onSubmit={submitReview}>
<Form.Group className="star-rating">
<Form.Label>Your Rating:</Form.Label>
{[...Array(5)].map((_, i) => (
<span
key={i}
className={`star ${
i < (hoveredStars() || stars()) ? "selected" : ""
}`}
onMouseOver={() => onMouseOver(i + 1)}
onMouseOut={onMouseOut}
onClick={() => onClick(i + 1)}
>
★
</span>
))}
</Form.Group>
<div className="input-group">
<Form.Label for="name">Name:</Form.Label>
<Form.Control
id="name"
type="text"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
className="form-input"
/>
</div>
<div className="input-group">
<Form.Label for="review">Review:</Form.Label>
<Form.Control
id="review"
as="textarea"
rows={3}
value={review()}
onInput={(e) => setReview(e.currentTarget.value)}
className="form-input"
/>
</div>
<Button variant="success" type="submit" disabled={review() === ""}>
Submit
</Button>
</Form>
</div>
);
}
export 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.
Modify 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:
// App.jsx
import { createSignal, createEffect } from "solid-js";
import { Container, Col, Row } from "solid-bootstrap";
import ReviewCard from "./components/ReviewCard";
import ReviewForm from "./components/ReviewForm"; // Import the ReviewForm component
import "./index.css";
function App() {
const [reviews, setReviews] = createSignal([]);
// Fetch reviews from Strapi backend
const fetchReviews = () => {
fetch("http://localhost:1337/api/customer-reviews/")
.then((response) => response.json())
.then((data) => {
if (Array.isArray(data.data)) {
setReviews(data.data);
} else {
console.error("Expected an array of reviews, but received:", data);
setReviews([]);
}
})
.catch((error) => console.error("Error fetching reviews:", error));
};
createEffect(() => {
fetchReviews();
});
const deleteReview = async (id) => {
try {
const response = await fetch(
`http://localhost:1337/api/customer-reviews/${id}`,
{
method: "DELETE",
},
);
if (!response.ok) {
throw new Error("Failed to delete review");
}
// Re-fetch reviews to reflect the deletion
fetchReviews();
} catch (error) {
console.error("Error deleting review:", error);
}
};
return (
<>
<Container fluid className="App text-light text-center">
<Col md={{ span: 6, offset: 3 }}>
<Row className="mt-5">
<Col>
<ReviewForm fetchReviews={fetchReviews} />
</Col>
</Row>
<Row className="mt-5">
<Col>
<div className="cards-container">
{Array.isArray(reviews()) &&
reviews().map((r, rIndex) => (
<ReviewCard
key={rIndex}
review={r}
onDelete={deleteReview}
/>
))}
</div>
</Col>
</Row>
</Col>
</Container>
</>
);
}
export default App;
Here is a breakdown of what the code above does:
-
Fetching reviews: The
fetchReview
function fetches the reviews from the Strapi backend using thefetch
API. It sends aGET
request to the Strapi endpoint and updates the reviews state with the fetched data. -
Review display: The app uses the
reviews
state to display individual reviews. It checks if thereviews
state holds reviews in the form of an array. If it does, it loops through each review object and renders these reviews inside theReviewCard
component, which is responsible for displaying them. -
Delete reviews: This function utilizes the
DeleteReview
function to delete reviews. It sends aDELETE
request to the Strapi backend with the reviewersID
. If the deletion is successful, it re-fetches the reviews to reflect the deletion.
Demo Time!
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.
Conclusion
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!
Resources
- Github Repo of the full frontend code.