After like three days of learning a few basics in React, I tested my newfound knowledge by doing a Frontend Mentor challenge.
Here's the challenge:
At first, my plan was to use plain JavaScript and HTML for the challenge, as usual. But then I had this lightbulb moment: "Wait a minute, why not try building this with my new React knowledge?" I mean, what better way to solidify what I've learned about components, props, JSX, and styling React apps than by actually creating something this functional?
So, I tried it, and congratulations to me—I finished my very first React project from Frontend Mentor!
Here's mine:
Let me show you how I did it...
Understanding the challenge && challenges I faced
First of all, I had to sit down and understand what I was about to build, since this was my first time building something from scratch in React.
Nothing too hard here; it was just an FAQ accordion, something I could close my eyes to and build with pure vanilla JavaScript, but this was React! 😔
The goal of the project was to show and hide an answer when those plus and minus buttons are clicked.
It was here that I discovered while trying to build it that I needed useState to achieve that toggling magic, but unfortunately, I haven't learned anything about it yet, so I had to learn it on YouTube from Web Dev Simplified. After much explanation and coding along, I finally got what it was and how to use it.
Another challenge I faced was that at first, I couldn't make that two-face background, but after much trial and error and a little frustration, I did it.
Now let me walk you through my building process:
Setting the project:
With a clear understanding of what I needed to do, it was time to set up the project. I decided to use the Create React App.
I opened my terminal and ran the following command:
npx create-react-app faq-accordion
After some time, it created my faq-accordion
folder with all the files and dependencies needed for a React application.
However, instead of using the default file structure, I started from scratch by deleting some of the unnecessary files, code, and folders that, of course, I wouldn't need for this small project. I also choose to keep things simple and code all my React components inside the index.js
file.
Inside the index.js
file, I imported the React and ReactDOM libraries and also my index.css:
import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css"
// my codes goes here
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<MyApp />
</React.StrictMode>
);
Then I went on with setting my folders right and creating a simple myApp
component that returns "Hello world" to make sure everything was working fine.
function MyApp(){
return <h1>Hello world</h1>;
}
const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MyApp />);
After which, I ran npm start in the terminal, which opened my default browser and gave me a preview of what I had coded: "Hello world", confirming that everything is working.
With everything set it was time to start writing my components. This was where the joy was.
Building the Components
As I was already familiar with React's components-based setup and other core things like props and JSX, I decided to take a structured approach to building my FAQ Accordion part.
First, I created an array of objects to store the questions and their answers. This would make it easy to map over the array and dynamically render the questions and answers in the UI.
I made it in this format for all the questions and answers:
const queAns = [
{
question: "What is Frontend Mentor, and how will it help me?",
answer: "Frontend Mentor offers real coding challenges to help developers improve their frontend coding skills with projects in HTML, CSS, and JavaScript. It's suitable for all levels and ideal for building a portfolio."
},
// ...
];
Having this data setup in place allowed me to focus on the component logic and showing without having to hard-code the questions and answers directly in the component.
With the data ready, I broke down the UI into three main components: Header, Main, and Question.
Header:
This one seemed straightforward—just a simple section to display a title for the FAQ. But even with something basic, I practiced creating a functional component and returning JSX.
function Header() {
return (
<header className="header">
<img src="/svgs/icon-star.svg" alt="star icon" />
<h1>FAQs</h1>
</header>
)
}
Then I updated myApp component by calling the Header component and main component that I will build later:
function MyApp() {
return (
<main className="container">
<Header />
<Main />
</main>
);
}
I know, not groundbreaking stuff. But for a React newbie, just being able to create and show a component felt like a small win!
Main component
Now, this is where things got interesting. The Main component would render the list of questions and answers. That's why I created the array of objects so as to map through it.
function Main() {
return (
<section>
{queAns.map((que) => (
<Question faqObject={que} key={que.question} />
))}
</section>
)
}
Question
The Header and Main parts were straightforward to create, as I had already understood things like functional components, JSX, and rendering arrays with the map function.
However, the real challenge (and learning chance) came with the Question component, which would handle the core functionality of toggling the visibility of the answer text and other functionalities.
First, I went on to create a global variable for the icons:
const plusIcon = "/svgs/icon-plus.svg";
const minusIcon = "/svgs/icon-minus.svg";
Then created my Question component:
function Question({ faqObject: { question, answer } }) {
const [currentIcon, setIcon] = useState(plusIcon); //VSCODE automatically imported useState hook for me
const handleOnClick = () => {
setIcon(currentIcon === plusIcon ? minusIcon : plusIcon);
};
const handleKeyDown = (e) => {
console.log(e.key);
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
handleOnClick();
}
};
return (
<div className="faq-box">
<h4>{question}</h4>
<img
className="icon"
src={currentIcon}
alt="icon plus"
onClick={handleOnClick}
onKeyDown={handleKeyDown}
tabIndex={0}
role="button"
aria-expanded={currentIcon === minusIcon}
/>
<p className={`answer ${currentIcon === plusIcon ? "hidden" : ""}`}>
{answer}
</p>
</div>
);
}
While I was familiar with props, the idea of useState was new to me.
At first, I felt a bit lost when I couldn't make that toggling magic and was tempted to go back to vanilla JavaScript. But after asking some questions on ChatGPT, it was then that I discovered I could use useState! And that's what made me to go watch it on YouTube and eventually achieve the code above.
I totally felt like a star…
Before I forget, here's the entire CSS that I was actually coding out as I was building the React components, which made it comfortable to follow through on the original Frontend Mentor challenge:
:root {
--clr-white: hsl(0, 0%, 100%);
--clr-main: hsl(292, 42%, 14%);
--clr-bg: hsl(276, 100%, 97%);
--clr-gray: hsl(292, 16%, 49%);
}
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: inherit;
}
html {
box-sizing: border-box;
font-size: 62.5%;
}
body {
font-size: 1.4rem;
font-family: "Work Sans", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
background-color: var(--clr-bg);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 1.5rem;
}
body::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 36vh;
background-image: url("/public/svgs/background-pattern-desktop.svg");
background-repeat: no-repeat;
background-size: cover;
}
.container {
max-width: 50rem;
background: var(--clr-white);
color: var(--clr-main);
padding: 2.5rem;
border-radius: 10px;
box-shadow: 0 2.4rem 4.8rem rgba(0, 0, 0, 0.1);
position: relative;
z-index: 9999;
}
.header {
display: flex;
column-gap: 2rem;
margin-bottom: 2rem;
}
h1 {
font-size: 4.8rem;
}
.faq-box {
margin-top: 1rem;
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
column-gap: 3rem;
row-gap: 1.5rem;
}
.faq-box:not(:last-child) {
border-bottom: 1.5px solid var(--clr-bg);
padding-bottom: 1.4rem;
}
h4 {
font-weight: 700;
margin: 0.8rem 0;
}
.answer {
font-size: inherit;
color: var(--clr-gray);
grid-column: 1 / -1;
}
.answer.hidden {
display: none;
}
.icon {
cursor: pointer;
width: 2.6rem;
height: 2.6rem;
}
.icon:hover {
padding: 0.1rem;
background-color: var(--clr-bg);
border-radius: 50%;
}
@media (max-width: 48em) {
body::before {
background-image: url("/public/svgs/background-pattern-mobile.svg");
}
h1 {
font-size: 3.2rem;
}
}
And that's all… My frontend Mentor challenge was totally working as intended.
Lesson learnt:
Building this FAQ thingy was a super awesome learning process for a React newbie like me! Along the way, I hit some road bumps but also had tons of "ah-ha!" moments that taught me valuable stuff moving forward.
This project showed me one important thing: Even if you don't know much about coding languages or frameworks, you can still build really cool stuff.
I also learned how powerful it is to learn on your own. When I didn't understand things like "useState", I didn't give up. Nope! I asked questions, played around, and kept trying until it finally clicked.
Styling with React was tricky too but I kept things nice and simple. Even making that two-toned background wasn't too bad once I figured it out.
You can see my FAQ live right here.
If you're new to React or already an experienced coder, I encourage you to try making stuff like this. Seeing your creation come alive is an incredible feeling that teaches you so much.
So feel free to shower me with compliments, advice, suggestions, or criticisms! I'm all ears because every bit of feedback helps me grow.
Top comments (2)
Awesome! You did a great job.
Thank you, Gaspard ❤️