Enhancing Practical Knowledge in Frontend Development with React.js: Creating a Portfolio Builder Print

  • 1

Enhancing Practical Knowledge in Frontend Development with React.js: Creating a Portfolio Builder

If you're aspiring to be a proficient frontend developer, working on practical projects is one of the best ways to level up your skills. This article will guide you through building a "Portfolio Builder" application using React.js in a MERN stack. The project will cover HTML, CSS, JavaScript, and Bootstrap for styling. Let's dive right in!

Prerequisite Tools:

1. Node.js and npm (Node Package Manager)
2. Create-React-App
3. Text Editor (VS Code, Sublime, etc.)

To ensure a successful start, you should have Node.js and npm installed. If not, download them [here](https://nodejs.org/).

Once you've installed Node.js and npm, you can install Create-React-App, a tool that allows you to create new React applications with ease. Run the following command:


npm install -g create-react-app

Now, we're ready to start our project!

Step 1: Create a New React Application

Navigate to the directory where you want your project to live and run the following command:


npx create-react-app portfolio-builder

Navigate into your new project:


cd portfolio-builder

Start the development server:


npm start

At this point, you should be able to see your new React application running at [http://localhost:3000](http://localhost:3000).

Step 2: Setting Up Your Application Structure

In the 'src' directory, we will keep our React components. Let's create a new 'components' folder. Within 'components', create 'Header.js', 'Footer.js', and 'PortfolioForm.js'.

Your directory structure should now look like this:


portfolio-builder
└───src
│ │ App.js
│ │ index.js
│ └───components
│ │ Header.js
│ │ Footer.js
│ │ PortfolioForm.js

Step 3: Creating Components

Header Component

In 'Header.js', we will define a simple navigation bar. We'll be using Bootstrap for styling.


import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

const Header = () => {
return (
<nav className="navbar navbar-dark bg-dark">
<div className="container-fluid">
<a className="navbar-brand" href="#">Portfolio Builder</a>
</div>
</nav>
)
}

export default Header;

Footer Component

Similarly, in 'Footer.js', we define a footer for our application.


import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

const Footer = () => {
return (
<footer className="footer bg-dark text-center text-white">
<div className="container p-4">
<p>© 2023 Portfolio Builder</p>
</div>
</footer>
)
}

export default Footer;

PortfolioForm Component

This is where the main functionality of our application will be. This form will take user inputs to generate the portfolio.


import React, { useState } from 'react';

const PortfolioForm = () => {
const [name, setName] = useState('');
// similar state declarations for other form fields...

const handleSubmit = (event) => {
event.preventDefault();
// here, we would handle form submission...
}

return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={e => setName(e.target.value)} />
</label>
{/* similar form fields for other data... */}
<input type="submit" value="Submit" />
</form>
)
}

export default PortfolioForm;

Step 4: Assembling the Application

Now we need to import and use these components inside 'App.js'.


import React from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import PortfolioForm from './components/PortfolioForm';

function App() {
return (
<div className="App">
<Header />
<PortfolioForm />
<Footer />
</div>
);
}

export default App;

And there you have it - a simple portfolio builder app using React.js! This basic structure should serve as a stepping stone towards more complex applications. To complete this project, you'll need to continue developing the `PortfolioForm` component, adding more fields, validating user input, and finally, displaying the portfolio. Remember to use React's concepts like state, props, and component lifecycle methods for a robust and dynamic application.

Step 5: Expanding the PortfolioForm Component

We want to collect more data about the user's portfolio, so let's add more fields to our form.


import React, { useState } from 'react';

const PortfolioForm = () => {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
const [skills, setSkills] = useState('');
const [projects, setProjects] = useState('');

const handleSubmit = (event) => {
event.preventDefault();
// to be implemented...
}

return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Bio:
<textarea value={bio} onChange={e => setBio(e.target.value)} />
</label>
<label>
Skills:
<input type="text" value={skills} onChange={e => setSkills(e.target.value)} />
</label>
<label>
Projects:
<input type="text" value={projects} onChange={e => setProjects(e.target.value)} />
</label>
<input type="submit" value="Submit" />
</form>
)
}

export default PortfolioForm;

Step 6: Handling Form Submission

When the user submits the form, we want to display the portfolio. For now, let's simply log the form data.


const handleSubmit = (event) => {
event.preventDefault();

console.log({
name,
bio,
skills,
projects,
});
}

Step 7: Displaying the Portfolio

To display the portfolio, we'll create a new `Portfolio` component. Let's also introduce a new piece of state in our `App` component to track the current portfolio.

In 'App.js':


import React, { useState } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import PortfolioForm from './components/PortfolioForm';
import Portfolio from './components/Portfolio'; // new import

function App() {
const [portfolio, setPortfolio] = useState(null);

const handleFormSubmit = (data) => {
setPortfolio(data);
}

return (
<div className="App">
<Header />
{portfolio ? <Portfolio data={portfolio} /> : <PortfolioForm onSubmit={handleFormSubmit} />}
<Footer />
</div>
);
}

export default App;

In 'PortfolioForm.js', update `handleSubmit` to call `onSubmit`:


const handleSubmit = (event) => {
event.preventDefault();

onSubmit({
name,
bio,
skills,
projects,
});
}

Lastly, create a new file 'Portfolio.js':


import React from 'react';

const Portfolio = ({ data }) => {
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
<h2>Skills</h2>
<p>{data.skills}</p>
<h2>Projects</h2>
<p>{data.projects}</p>
</div>
)
}

export default Portfolio;

And that's it! You've now built a basic Portfolio Builder application with React.js. This application lets the user enter their portfolio details and displays them. From here, you can continue to add features, such as form validation, styling the portfolio, adding the ability to edit the portfolio, and more!

Step 8: Form Validation

Before we submit the form, we want to make sure the user has filled out all the necessary fields. If a field is empty, we'll display an error message.

Let's modify our `PortfolioForm` component to include form validation:


import React, { useState } from 'react';

const PortfolioForm = ({ onSubmit }) => {
const [name, setName] = useState('');
const [bio, setBio] = useState('');
const [skills, setSkills] = useState('');
const [projects, setProjects] = useState('');
const [errors, setErrors] = useState({});

const handleSubmit = (event) => {
event.preventDefault();

const newErrors = {};

if (!name) newErrors.name = 'Name is required.';
if (!bio) newErrors.bio = 'Bio is required.';
if (!skills) newErrors.skills = 'Skills are required.';
if (!projects) newErrors.projects = 'Projects are required.';

if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
} else {
onSubmit({
name,
bio,
skills,
projects,
});
}
}

return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={e => setName(e.target.value)} />
</label>
{errors.name && <p>{errors.name}</p>}
{/* repeat for other fields... */}
<input type="submit" value="Submit" />
</form>
)
}

export default PortfolioForm;

With this code, if the user tries to submit the form without filling out all the fields, they will see an error message for each missing field.

Step 9: Editing the Portfolio

Next, let's give the user the ability to edit their portfolio after they've created it. In our `Portfolio` component, we'll add an "Edit" button that, when clicked, sets the current portfolio back to `null`, which causes the form to be displayed again.

First, update the `Portfolio` component:


import React from 'react';

const Portfolio = ({ data, onEdit }) => {
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
<h2>Skills</h2>
<p>{data.skills}</p>
<h2>Projects</h2>
<p>{data.projects}</p>
<button onClick={onEdit}>Edit</button>
</div>
)
}

export default Portfolio;

Then, update the `App` component to handle the `onEdit` prop:


function App() {
const [portfolio, setPortfolio] = useState(null);

const handleFormSubmit = (data) => {
setPortfolio(data);
}

const handleEditClick = () => {
setPortfolio(null);
}

return (
<div className="App">
<Header />
{portfolio ? <Portfolio data={portfolio} onEdit={handleEditClick} /> : <PortfolioForm onSubmit={handleFormSubmit} />}
<Footer />
</div>
);
}

With these additions, our Portfolio Builder application now includes form validation and the ability to edit the portfolio. This was a great exercise to familiarize yourself with React.js concepts and practices! For further enhancements, you could add styling to improve the look and feel of the application, introduce routes to navigate between different pages, or even integrate a backend to store the portfolios.

Step 10: Styling the Form

Let's update our `PortfolioForm` component to include Bootstrap classes:


import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

const PortfolioForm = ({ onSubmit }) => {
//...previous code

return (
<form onSubmit={handleSubmit} className="container mt-5">
<div className="mb-3">
<label htmlFor="name" className="form-label">Name:</label>
<input type="text" id="name" value={name} onChange={e => setName(e.target.value)} className="form-control"/>
{errors.name && <p className="text-danger">{errors.name}</p>}
</div>
{/* similar for other fields */}
<button type="submit" className="btn btn-primary">Submit</button>
</form>
)
}

Step 11: Styling the Portfolio

Similarly, let's update the `Portfolio` component:


import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

const Portfolio = ({ data, onEdit }) => {
return (
<div className="container mt-5">
<h1>{data.name}</h1>
<p>{data.bio}</p>
<h2>Skills</h2>
<p>{data.skills}</p>
<h2>Projects</h2>
<p>{data.projects}</p>
<button onClick={onEdit} className="btn btn-warning">Edit</button>
</div>
)
}

Step 12: Adding a Navigation Menu

You can add a navigation menu to the `Header` component. This will allow the user to navigate through different sections of the portfolio, such as the "About", "Skills", and "Projects" sections. Although the navigation won't do much for our simple app, it's good to know how you might structure a larger application.


import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

const Header = () => {
return (
<nav className="navbar navbar-expand-lg navbar-dark bg-dark">
<div className="container-fluid">
<a className="navbar-brand" href="#">Portfolio Builder</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<a className="nav-link active" aria-current="page" href="#about">About</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#skills">Skills</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#projects">Projects</a>
</li>
</ul>
</div>
</div>
</nav>
)
}

export default Header;

By styling our components and adding a navigation menu, we've made our Portfolio Builder much more user-friendly and visually appealing. While there is still a lot you could do to enhance this application, we've covered a broad range of concepts that you can use as a springboard for more complex projects. 

Let's integrate a simple backend using the Node.js and Express.js parts of our MERN stack. This backend will allow users to save their portfolios and retrieve them later.

Firstly, we'll need to set up a new Express.js application. Here's the basic structure:


const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors());
app.use(express.json());

let portfolios = [];

app.post('/portfolio', (req, res) => {
portfolios.push(req.body);
res.status(200).send();
});

app.get('/portfolio', (req, res) => {
res.json(portfolios);
});

app.listen(3001, () => {
console.log('Server running on port 3001');
});

You would need to run `npm init -y` to initialize a new Node.js project and then install the necessary packages (`express` and `cors`) using `npm install express cors`.

This simple server accepts POST requests to `/portfolio` to create new portfolios and GET requests to `/portfolio` to retrieve all portfolios.

Now, let's modify our frontend to communicate with this backend. We will use the built-in `fetch` function to make HTTP requests.

Add the following function in `App.js`:


const fetchPortfolios = async () => {
const response = await fetch('http://localhost:3001/portfolio');
const data = await response.json();
setPortfolios(data);
}

And call it in a `useEffect` hook:


useEffect(() => {
fetchPortfolios();
}, []);

Next, modify the `handleFormSubmit` function to also send a POST request:


const handleFormSubmit = async (data) => {
const response = await fetch('http://localhost:3001/portfolio', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});

if (response.ok) {
fetchPortfolios();
}
}

Now, when the form is submitted, the data is sent to the server and saved. The portfolios are then re-fetched to include the newly created one.

That's it! You've now integrated a simple backend into your portfolio builder application. Of course, this is a very basic setup and you'd want a more robust solution for a production application, including persistent storage in a database and error handling.

Remember to run your server alongside your frontend. You can use a tool like `nodemon` to automatically restart your server when your code changes. You can install it globally with `npm install -g nodemon` and then run your server with `nodemon server.js`.

 


Was this answer helpful?

« Back