Creating a resume builder with React, NodeJS and AI 🚀
In this article, you'll learn how to create a resume builder using React, Node.js, and the OpenAI API. What's better to look for a job and say you have build a job resume builder with AI to do so? 🤩
A small request 🥺
I produce content weekly, and your support helps so much to create more content. Please support me by clicking the “Love” button. You probably want to “Save” this article also, so you can just click both buttons. Thank you very very much! ❤️
Introduction to the OpenAI API
GPT-3 is a type of artificial intelligence program developed by OpenAI that is really good at understanding and processing human language. It has been trained on a huge amount of text data from the internet, which allows it to generate high-quality responses to a wide range of language-related tasks.
For this article we will use OpenAI GPT3.
Once the ChatGPT API is out, I will create another article using it 🤗
I have been a big fan of OpenAI from the day they released their first API, I have turned to one of the employees and sent them a nice request to get access to the beta version of GPT3, and I got it 😅
That’s me in Dec 30, 2020, Begging for access.
Novu – the first open-source notification infrastructure
Just a quick background about us. Novu provides a unified API that makes it simple to send notifications through multiple channels, including In-App, Push, Email, SMS, and Chat. With Novu, you can create custom workflows and define conditions for each channel, ensuring that your notifications are delivered in the most effective way possible.
I would be super happy if you could give us a star! And let me also know in the comments ❤️
https://github.com/novuhq/novu
Project Setup
Here, I’ll guide you through creating the project environment for the web application. We’ll use React.js for the front end and Node.js for the backend server.
Create the project folder for the web application by running the code below:
1mkdir resume-builder
2cd resume-builder
3mkdir client server
Setting up the Node.js server
Navigate into the server folder and create a package.json
file.
1cd server & npm init -y
Install Express, Nodemon, and the CORS library
1npm install express cors nodemon
ExpressJS is a fast, minimalist framework that provides several features for building web applications in Node.js, CORS is a Node.js package that allows communication between different domains, and Nodemon is a Node.js tool that automatically restarts the server after detecting file changes.
Create an index.js
file – the entry point to the web server.
1touch index.js
Set up a Node.js server using Express.js. The code snippet below returns a JSON object when you visit the http://localhost:4000/api
in your browser.
1//👇🏻index.js
2const express = require("express");
3const cors = require("cors");
4const app = express();
5const PORT = 4000;
6
7app.use(express.urlencoded({ extended: true }));
8app.use(express.json());
9app.use(cors());
10
11app.get("/api", (req, res) => {
12 res.json({
13 message: "Hello world",
14 });
15});
16
17app.listen(PORT, () => {
18 console.log(`Server listening on ${PORT}`);
19});
Configure Nodemon by adding the start command to the list of scripts in the package.json
file. The code snippet below starts the server using Nodemon.
1//In server/package.json
2
3"scripts": {
4 "test": "echo \"Error: no test specified\" && exit 1",
5 "start": "nodemon index.js"
6 },
Congratulations! You can now start the server by using the command below.
1npm start
Setting up the React application
Navigate into the client folder via your terminal and create a new React.js project.
1cd client
2npx create-react-app ./
Install Axios and React Router. React Router is a JavaScript library that enables us to navigate between pages in a React application. Axios is a promise-based Node.js HTTP client for performing asynchronous requests.
1npm install axios react-router-dom
Delete the redundant files, such as the logo and the test files from the React app, and update the App.js
file to display Hello World as below.
1function App() {
2 return (
3 <div>
4 <p>Hello World!</p>
5 </div>
6 );
7}
8export default App;
Navigate into the src/index.css
file and copy the code below. It contains all the CSS required for styling this project.
1@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap");
2
3* {
4 font-family: "Space Grotesk", sans-serif;
5 box-sizing: border-box;
6 margin: 0;
7 padding: 0;
8}
9form {
10 padding: 10px;
11 width: 80%;
12 display: flex;
13 flex-direction: column;
14}
15input {
16 margin-bottom: 15px;
17 padding: 10px 20px;
18 border-radius: 3px;
19 outline: none;
20 border: 1px solid #ddd;
21}
22h3 {
23 margin: 15px 0;
24}
25button {
26 padding: 15px;
27 cursor: pointer;
28 outline: none;
29 background-color: #5d3891;
30 border: none;
31 color: #f5f5f5;
32 font-size: 16px;
33 font-weight: bold;
34 border-radius: 3px;
35}
36.app {
37 min-height: 100vh;
38 display: flex;
39 flex-direction: column;
40 align-items: center;
41 justify-content: center;
42 padding: 30px;
43}
44.app > p {
45 margin-bottom: 30px;
46}
47.nestedContainer {
48 display: flex;
49 align-items: center;
50 justify-content: space-between;
51 width: 100%;
52}
53.companies {
54 display: flex;
55 flex-direction: column;
56 width: 39%;
57}
58.currentInput {
59 width: 95%;
60}
61#photo {
62 width: 50%;
63}
64#addBtn {
65 background-color: green;
66 margin-right: 5px;
67}
68#deleteBtn {
69 background-color: red;
70}
71.container {
72 min-height: 100vh;
73 padding: 30px;
74}
75.header {
76 width: 80%;
77 margin: 0 auto;
78 min-height: 10vh;
79 background-color: #e8e2e2;
80 padding: 30px;
81 border-radius: 3px 3px 0 0;
82 display: flex;
83 align-items: center;
84 justify-content: space-between;
85}
86.resumeTitle {
87 opacity: 0.6;
88}
89.headerTitle {
90 margin-bottom: 15px;
91}
92.resumeImage {
93 vertical-align: middle;
94 width: 150px;
95 height: 150px;
96 border-radius: 50%;
97}
98.resumeBody {
99 width: 80%;
100 margin: 0 auto;
101 padding: 30px;
102 min-height: 80vh;
103 border: 1px solid #e0e0ea;
104}
105.resumeBodyTitle {
106 margin-bottom: 5px;
107}
108.resumeBodyContent {
109 text-align: justify;
110 margin-bottom: 30px;
111}
Building the application user interface
Here, we’ll create the user interface for the resume builder application to enable users to submit their information and print the AI-generated resume.
Create a components folder within the client/src
folder containing the Home.js
, Loading.js
, Resume.js
, ErrorPage.js
files.
1cd client/src
2mkdir components
3touch Home.js Loading.js Resume.js ErrorPage.js
From the code snippet above:
- The
Home.js
file renders the form field to enable users to enter the necessary information. - The
Loading.js
contains the component shown to the user when the request is pending. - The
Resume.js
displays the AI-generated resume to the user. - The
ErrorPage.js
is shown when an error occurs.
Update the App.js
file to render the components using React Router.
1import React from "react";
2import { BrowserRouter, Routes, Route } from "react-router-dom";
3import Home from "./components/Home";
4import Resume from "./components/Resume";
5
6const App = () => {
7 return (
8 <div>
9 <BrowserRouter>
10 <Routes>
11 <Route path='/' element={<Home />} />
12 <Route path='/resume' element={<Resume />} />
13 </Routes>
14 </BrowserRouter>
15 </div>
16 );
17};
18
19export default App;
The Home page
Here, you’ll learn how to build a form layout that can send images via HTTP request and dynamically add and remove input fields.
First, update the Loading component to render the code snippet below, shown to the user when the resume is pending.
1import React from "react";
2
3const Loading = () => {
4 return (
5 <div className='app'>
6 <h1>Loading, please wait...</h1>
7 </div>
8 );
9};
10
11export default Loading;
Next, update the ErrorPage.js
file to display the component below when users navigate directly to the resume page.
1import React from "react";
2import { Link } from "react-router-dom";
3
4const ErrorPage = () => {
5 return (
6 <div className='app'>
7 <h3>
8 You've not provided your details. Kindly head back to the{" "}
9 <Link to='/'>homepage</Link>.
10 </h3>
11 </div>
12 );
13};
14
15export default ErrorPage;
Copy the code snippet below into the Home.js
file
1import React, { useState } from "react";
2import Loading from "./Loading";
3
4const Home = () => {
5 const [fullName, setFullName] = useState("");
6 const [currentPosition, setCurrentPosition] = useState("");
7 const [currentLength, setCurrentLength] = useState(1);
8 const [currentTechnologies, setCurrentTechnologies] = useState("");
9 const [headshot, setHeadshot] = useState(null);
10 const [loading, setLoading] = useState(false);
11
12 const handleFormSubmit = (e) => {
13 e.preventDefault();
14 console.log({
15 fullName,
16 currentPosition,
17 currentLength,
18 currentTechnologies,
19 headshot,
20 });
21 setLoading(true);
22 };
23 //👇🏻 Renders the Loading component you submit the form
24 if (loading) {
25 return <Loading />;
26 }
27 return (
28 <div className='app'>
29 <h1>Resume Builder</h1>
30 <p>Generate a resume with ChatGPT in few seconds</p>
31 <form
32 onSubmit={handleFormSubmit}
33 method='POST'
34 encType='multipart/form-data'
35 >
36 <label htmlFor='fullName'>Enter your full name</label>
37 <input
38 type='text'
39 required
40 name='fullName'
41 id='fullName'
42 value={fullName}
43 onChange={(e) => setFullName(e.target.value)}
44 />
45 <div className='nestedContainer'>
46 <div>
47 <label htmlFor='currentPosition'>Current Position</label>
48 <input
49 type='text'
50 required
51 name='currentPosition'
52 className='currentInput'
53 value={currentPosition}
54 onChange={(e) => setCurrentPosition(e.target.value)}
55 />
56 </div>
57 <div>
58 <label htmlFor='currentLength'>For how long? (year)</label>
59 <input
60 type='number'
61 required
62 name='currentLength'
63 className='currentInput'
64 value={currentLength}
65 onChange={(e) => setCurrentLength(e.target.value)}
66 />
67 </div>
68 <div>
69 <label htmlFor='currentTechnologies'>Technologies used</label>
70 <input
71 type='text'
72 required
73 name='currentTechnologies'
74 className='currentInput'
75 value={currentTechnologies}
76 onChange={(e) => setCurrentTechnologies(e.target.value)}
77 />
78 </div>
79 </div>
80 <label htmlFor='photo'>Upload your headshot image</label>
81 <input
82 type='file'
83 name='photo'
84 required
85 id='photo'
86 accept='image/x-png,image/jpeg'
87 onChange={(e) => setHeadshot(e.target.files[0])}
88 />
89
90 <button>CREATE RESUME</button>
91 </form>
92 </div>
93 );
94};
95
96export default Home;
The code snippet renders the form field below. It accepts the full name and current work experience – (year, position, title) and allows the user to upload a headshot image via the form field.
Lastly, you need to accept the user’s previous work experience. So, add a new state that holds the array of job descriptions.
1const [companyInfo, setCompanyInfo] = useState([{ name: "", position: "" }]);
Add the following functions which help with updating the state.
1//👇🏻 updates the state with user's input
2const handleAddCompany = () =>
3 setCompanyInfo([...companyInfo, { name: "", position: "" }]);
4
5//👇🏻 removes a selected item from the list
6const handleRemoveCompany = (index) => {
7 const list = [...companyInfo];
8 list.splice(index, 1);
9 setCompanyInfo(list);
10};
11//👇🏻 updates an item within the list
12const handleUpdateCompany = (e, index) => {
13 const { name, value } = e.target;
14 const list = [...companyInfo];
15 list[index][name] = value;
16 setCompanyInfo(list);
17};
The handleAddCompany
updates the companyInfo
state with the user’s input, handleRemoveCompany
is used to remove an item from the list of data provided, and the handleUpdateCompany
updates the item properties – (name and position) within the list.
Next, render the UI elements for the work experience section.
1return (
2 <div className='app'>
3 <h3>Companies you've worked at</h3>
4 <form>
5 {/*--- other UI tags --- */}
6 {companyInfo.map((company, index) => (
7 <div className='nestedContainer' key={index}>
8 <div className='companies'>
9 <label htmlFor='name'>Company Name</label>
10 <input
11 type='text'
12 name='name'
13 required
14 onChange={(e) => handleUpdateCompany(e, index)}
15 />
16 </div>
17 <div className='companies'>
18 <label htmlFor='position'>Position Held</label>
19 <input
20 type='text'
21 name='position'
22 required
23 onChange={(e) => handleUpdateCompany(e, index)}
24 />
25 </div>
26
27 <div className='btn__group'>
28 {companyInfo.length - 1 === index && companyInfo.length < 4 && (
29 <button id='addBtn' onClick={handleAddCompany}>
30 Add
31 </button>
32 )}
33 {companyInfo.length > 1 && (
34 <button id='deleteBtn' onClick={() => handleRemoveCompany(index)}>
35 Del
36 </button>
37 )}
38 </div>
39 </div>
40 ))}
41
42 <button>CREATE RESUME</button>
43 </form>
44 </div>
45);
The code snippet maps through the elements within the companyInfo
array and displays them on the webpage. The handleUpdateCompany
function runs when a user updates the input field, then handleRemoveCompany
removes an item from the list of elements, and the handleAddCompany
adds a new input field.
The Resume page
This page shows the resume generated from the OpenAI API in a printable format. Copy the code below into the Resume.js
file. We’ll update its content later in this tutorial.
1import React from "react";
2import ErrorPage from "./ErrorPage";
3
4const Resume = ({ result }) => {
5 if (JSON.stringify(result) === "{}") {
6 return <ErrorPage />;
7 }
8
9 const handlePrint = () => alert("Print Successful!");
10 return (
11 <>
12 <button onClick={handlePrint}>Print Page</button>
13 <main className='container'>
14 <p>Hello!</p>
15 </main>
16 </>
17 );
18};
How to submit images via forms in Node.js
Here, I’ll guide you on how to submit the form data to the Node.js server. Since the form contains images, we’ll need to set up Multer on the Node.js server.
💡 Multer is a Node.js middleware used for uploading files to the server.
Setting up Multer
Run the code below to install Multer
1npm install multer
Ensure the form on the frontend application has the method and encType
attributes, because Multer only process forms which are multpart.
1<form method="POST" enctype="multipart/form-data"></form>
Import the Multer and the Node.js path packages into the index.js
file
1const multer = require("multer");
2const path = require("path");
Copy the code below into the index.js
to configure Multer.
1app.use("/uploads", express.static("uploads"));
2
3const storage = multer.diskStorage({
4 destination: (req, file, cb) => {
5 cb(null, "uploads");
6 },
7 filename: (req, file, cb) => {
8 cb(null, Date.now() + path.extname(file.originalname));
9 },
10});
11
12const upload = multer({
13 storage: storage,
14 limits: { fileSize: 1024 * 1024 * 5 },
15});
- From the code snippet above:
- The
app.use()
function enables Node.js to serve the contents of anuploads
folder. The contents refer to static files such as images, CSS, and JavaScript files. - The
storage
variable containingmulter.diskStorage
gives us full control of storing the images. The function above stores the images in the upload folder and renames the image to its upload time (to prevent filename conflicts). - The upload variable passes the configuration to Multer and set a size limit of 5MB for the images.
- The
Create the uploads
folder on the server. This is where the images will be saved.
1mkdir uploads
How to upload images to a Node.js server
Add a route that accepts all the form inputs from the React app. The upload.single("headshotImage")
function adds the image uploaded via the form to the uploads
folder.
1app.post("/resume/create", upload.single("headshotImage"), async (req, res) => {
2 const {
3 fullName,
4 currentPosition,
5 currentLength,
6 currentTechnologies,
7 workHistory,
8 } = req.body;
9
10 console.log(req.body);
11
12 res.json({
13 message: "Request successful!",
14 data: {},
15 });
16});
Update the handleFormSubmit
function within the Home.js
component to submit the form data to the Node.js server.
1import axios from "axios";
2
3const handleFormSubmit = (e) => {
4 e.preventDefault();
5
6 const formData = new FormData();
7 formData.append("headshotImage", headshot, headshot.name);
8 formData.append("fullName", fullName);
9 formData.append("currentPosition", currentPosition);
10 formData.append("currentLength", currentLength);
11 formData.append("currentTechnologies", currentTechnologies);
12 formData.append("workHistory", JSON.stringify(companyInfo));
13 axios
14 .post("http://localhost:4000/resume/create", formData, {})
15 .then((res) => {
16 if (res.data.message) {
17 console.log(res.data.data);
18 navigate("/resume");
19 }
20 })
21 .catch((err) => console.error(err));
22 setLoading(true);
23};
The code snippet above creates a key/value pair representing the form fields and their values which are sent via Axios to the API endpoint on the server. If there is a response, it logs the response and redirect the user to the Resume page.
How to communicate with the OpenAI API in Node.js
In this section, you’ll learn how to communicate with the OpenAI API within the Node.js server.
We’ll send the user’s information to the API to generate a profile summary, job description, and achievements or related activities completed at the previous organisations. To accomplish this:
Install the OpenAI API Node.js library by running the code below.
1npm install openai
Log in or create an OpenAI account here.
Click Personal
on the navigation bar and select View API keys
from the menu bar to create a new secret key.
Copy the API Key somewhere safe on your computer; we’ll use it shortly.
Configure the API by copying the code below into the index.js
file.
1const { Configuration, OpenAIApi } = require("openai");
2
3const configuration = new Configuration({
4 apiKey: "<YOUR_API_KEY>",
5});
6
7const openai = new OpenAIApi(configuration);
Create a function that accepts a text (prompt) as a parameter and returns an AI-generated result.
1const GPTFunction = async (text) => {
2 const response = await openai.createCompletion({
3 model: "text-davinci-003",
4 prompt: text,
5 temperature: 0.6,
6 max_tokens: 250,
7 top_p: 1,
8 frequency_penalty: 1,
9 presence_penalty: 1,
10 });
11 return response.data.choices[0].text;
12};
The code snippet above uses the text-davinci-003
model to generate an appropriate answer to the prompt. The other key values helps us generate the specific type of response we need.
Update the /resume/create
route as done below.
1app.post("/resume/create", upload.single("headshotImage"), async (req, res) => {
2 const {
3 fullName,
4 currentPosition,
5 currentLength,
6 currentTechnologies,
7 workHistory, //JSON format
8 } = req.body;
9
10 const workArray = JSON.parse(workHistory); //an array
11
12 //👇🏻 group the values into an object
13 const newEntry = {
14 id: generateID(),
15 fullName,
16 image_url: `http://localhost:4000/uploads/${req.file.filename}`,
17 currentPosition,
18 currentLength,
19 currentTechnologies,
20 workHistory: workArray,
21 };
22});
The code snippet above accepts the form data from the client, converts the workHistory
to its original data structure (array), and puts them all into an object.
Next, create the prompts you want to pass into the GPTFunction
.
1//👇🏻 loops through the items in the workArray and converts them to a string
2const remainderText = () => {
3 let stringText = "";
4 for (let i = 0; i < workArray.length; i++) {
5 stringText += ` ${workArray[i].name} as a ${workArray[i].position}.`;
6 }
7 return stringText;
8};
9//👇🏻 The job description prompt
10const prompt1 = `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${currentLength} years). \n I write in the technolegies: ${currentTechnologies}. Can you write a 100 words description for the top of the resume(first person writing)?`;
11//👇🏻 The job responsibilities prompt
12const prompt2 = `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${currentLength} years). \n I write in the technolegies: ${currentTechnologies}. Can you write 10 points for a resume on what I am good at?`;
13//👇🏻 The job achievements prompt
14const prompt3 = `I am writing a resume, my details are \n name: ${fullName} \n role: ${currentPosition} (${currentLength} years). \n During my years I worked at ${
15 workArray.length
16} companies. ${remainderText()} \n Can you write me 50 words for each company seperated in numbers of my succession in the company (in first person)?`;
17
18//👇🏻 generate a GPT-3 result
19const objective = await GPTFunction(prompt1);
20const keypoints = await GPTFunction(prompt2);
21const jobResponsibilities = await GPTFunction(prompt3);
22//👇🏻 put them into an object
23const chatgptData = { objective, keypoints, jobResponsibilities };
24//👇🏻log the result
25console.log(chatgptData);
- From the code snippet above:
- The
remainderText
function loops through the array of work history and returns a string data type of all work experiences. - Then, there are three prompts with instructions on what is needed from the GPT-3 API.
- Next, you store the results in an object and log them to the console.
- The
Lastly, return the AI-generated result and the information the users entered. You can also create an array representing the database that stores results as done below.
1let database = [];
2
3app.post("/resume/create", upload.single("headshotImage"), async (req, res) => {
4 //...other code statements
5 const data = { ...newEntry, ...chatgptData };
6 database.push(data);
7
8 res.json({
9 message: "Request successful!",
10 data,
11 });
12});
Displaying the response from the OpenAI API
In this section, I’ll guide you through displaying the results generated from the OpenAI API in a readable and printable format on a web page.
Create a React state within the App.js
file. The state will hold the results sent from the Node.js server.
1import React, { useState } from "react";
2import { BrowserRouter, Routes, Route } from "react-router-dom";
3import Home from "./components/Home";
4import Resume from "./components/Resume";
5
6const App = () => {
7 //👇🏻 state holding the result
8 const [result, setResult] = useState({});
9
10 return (
11 <div>
12 <BrowserRouter>
13 <Routes>
14 <Route path='/' element={<Home setResult={setResult} />} />
15 <Route path='/resume' element={<Resume result={result} />} />
16 </Routes>
17 </BrowserRouter>
18 </div>
19 );
20};
21
22export default App;
From the code snippet above, only setResult
is passed as a prop into the Home component and only result
for the Resume component. setResult
updates the value of the result once the form is submitted and the request is successful, while result
contains the response retrieved from the server, shown within the Resume component.
Update the result
state within the Home component after the form is submitted and the request is successful.
1const Home = ({ setResult }) => {
2 const handleFormSubmit = (e) => {
3 e.preventDefault();
4
5 //...other code statements
6 axios
7 .post("http://localhost:4000/resume/create", formData, {})
8 .then((res) => {
9 if (res.data.message) {
10 //👇🏻 updates the result object
11 setResult(res.data.data);
12 navigate("/resume");
13 }
14 })
15 .catch((err) => console.error(err));
16 setLoading(true);
17 };
18 return <div></div>;
19};
Update the Resume component as done below to preview the result within the React app.
1import ErrorPage from "./ErrorPage";
2
3const Resume = ({ result }) => {
4 //👇🏻 function that replaces the new line with a break tag
5 const replaceWithBr = (string) => {
6 return string.replace(/\n/g, "<br />");
7 };
8
9 //👇🏻 returns an error page if the result object is empty
10 if (JSON.stringify(result) === "{}") {
11 return <ErrorPage />;
12 }
13
14 const handlePrint = () => alert("Printing");
15
16 return (
17 <>
18 <button onClick={handlePrint}>Print Page</button>
19 <main className='container' ref={componentRef}>
20 <header className='header'>
21 <div>
22 <h1>{result.fullName}</h1>
23 <p className='resumeTitle headerTitle'>
24 {result.currentPosition} ({result.currentTechnologies})
25 </p>
26 <p className='resumeTitle'>
27 {result.currentLength}year(s) work experience
28 </p>
29 </div>
30 <div>
31 <img
32 src={result.image_url}
33 alt={result.fullName}
34 className='resumeImage'
35 />
36 </div>
37 </header>
38 <div className='resumeBody'>
39 <div>
40 <h2 className='resumeBodyTitle'>PROFILE SUMMARY</h2>
41 <p
42 dangerouslySetInnerHTML={{
43 __html: replaceWithBr(result.objective),
44 }}
45 className='resumeBodyContent'
46 />
47 </div>
48 <div>
49 <h2 className='resumeBodyTitle'>WORK HISTORY</h2>
50 {result.workHistory.map((work) => (
51 <p className='resumeBodyContent' key={work.name}>
52 <span style={{ fontWeight: "bold" }}>{work.name}</span> -{" "}
53 {work.position}
54 </p>
55 ))}
56 </div>
57 <div>
58 <h2 className='resumeBodyTitle'>JOB PROFILE</h2>
59 <p
60 dangerouslySetInnerHTML={{
61 __html: replaceWithBr(result.jobResponsibilities),
62 }}
63 className='resumeBodyContent'
64 />
65 </div>
66 <div>
67 <h2 className='resumeBodyTitle'>JOB RESPONSIBILITIES</h2>
68 <p
69 dangerouslySetInnerHTML={{
70 __html: replaceWithBr(result.keypoints),
71 }}
72 className='resumeBodyContent'
73 />
74 </div>
75 </div>
76 </main>
77 </>
78 );
79};
The code snippet above displays the result on the webpage according to the specified layout. The function replaceWithBr
replaces every new line (\n) with a break tag, and the handlePrint
function will enable users to print the resume.
How to print React pages using the React-to-print package
Here, you’ll learn how to add a print button to the web page that enables users to print the resume via the React-to-print package.
💡 React-to-print is a simple JavaScript package that enables you to print the content of a React component without tampering with the component CSS styles.
Run the code below to install the package
1npm install react-to-print
Import the library within the Resume.js
file and add the useRef
hook.
1import { useReactToPrint } from "react-to-print";
2import React, { useRef } from "react";
Update the Resume.js
file as done below.
1const Resume = ({ result }) => {
2 const componentRef = useRef();
3
4 const handlePrint = useReactToPrint({
5 content: () => componentRef.current,
6 documentTitle: `${result.fullName} Resume`,
7 onAfterPrint: () => alert("Print Successful!"),
8 });
9 //...other function statements
10 return (
11 <>
12 <button onClick={handlePrint}>Print Page</button>
13 <main className='container' ref={componentRef}>
14 {/*---other code statements---*/}
15 </main>
16 </>
17 );
18};
The handlePrint
function prints the elements within the componentRef
– main tag, sets the document’s name to the user’s full name, and runs the alert function when a user prints the form.
Congratulations! You’ve completed the project for this tutorial.
Here is a sample of the result gotten from the project:
Conclusion
So far, you’ve learnt:
- what OpenAI GPT-3 is,
- how to upload images via forms in a Node.js and React.js application,
- how to interact with the OpenAI GPT-3 API, and
- how to print React web pages via the React-to-print library.
This tutorial walks you through an example of an application you can build using the OpenAI API. With the API, you can create powerful applications useful in various fields, such as translators, Q&A, code explanation or generation, etc.
The source code for this tutorial is available here:
https://github.com/novuhq/blog/tree/main/resume-builder-with-react-chatgpt-nodejs
Thank you for reading!
Help me out!
If you feel like this article helped you, I would be super happy if you could give us a star! And let me also know in the comments ❤️