Projects People Resources Semesters Blog About
🦋

Building Your First Site (Profile Generator)

Last Updated: 31 December, 2022

📊 Table of Contents

🥅 The Goal

We will be making a Profile Generator to generate Profile Cards for Oasis members. Below is a screenshot of what we’ll be building. Users enter information in the blue section and then click Generate!. Upon clicking, the green section appears. This tutorial won’t cover every step, but the sample code is available below too so you can follow along there.

📦 Setup

First, launch VS Code, setup your repo, and run create-react-app. See

,
Version Control
Version Control
, and/or
React Basics (Setup, State, & Props)
React Basics (Setup, State, & Props)
if you need some help with this.

Now that we’re ready to code, let’s start by blocking out our main components. From the example, we see there are three clear sections: the header, input form, and output Let’s reflect this in code:

// App.js
import Header from "./Header"; // import each of your 3 components
import Form from "./Form";
import Output from "./Output";

export default function App() {
	return (
    <div>
      <Header />
      <Form />
      <Output />
    </div>
  );
}


// Header.js
export default function Header() { /* ... */ }

// ...

While we’re at it, let’s think about which components need access to which properties. We need four state variables to track values for each input, and one to decide if the output should be shown.

// App.js
import { useState } from "react"; // required to use state
import Header from "./Header"; // import each component
import Form from "./Form";
import Output from "./Output";

export default function App() {
	// input field values
  const [photo, setPhoto] = useState(null);
  const [name, setName] = useState("");
  const [title, setTitle] = useState("");
  const [fact, setFact] = useState("");

	return (
    <div>
      <Header />
      <Form />
      <Output />
    </div>
  );
}

Not every variable needs to be passed to every component. The Form needs everything, but the Output only needs photo, name, title, and fact, and the Header doesn’t need anything. It is important to only provide each component the data it actually needs.

// App.js
import { useState } from "react"; // required to use state
import Header from "./Header"; // import each component
import Form from "./Form";
import Output from "./Output";

export default function App() {
	// input field values
  const [photo, setPhoto] = useState(null);
  const [name, setName] = useState("");
  const [title, setTitle] = useState("");
  const [fact, setFact] = useState("");

  // whether to show the output
	const [showOutput, setShowOutput] = useState(false);

	return (
    <div>
      <Header />
      <Form
				photo={photo} setPhoto={setPhoto}
				name={name} setName={setName}
				title={title} setTitle={setTitle}
				fact={fact} setFact={setFact}
				showOutput={showOutput} setShowOutput={setShowOutput}
			/>
      <Output photo={photo} name={name} title={title} fact={fact} />
    </div>
  );
}

🔠 Header

The header is a quick component to set up. It contains only h1 and p elements:

export default function Header() {
  return (
    <>
      <h1>Profile Generator</h1>
      <p>Generate profile cards for Oasis members.</p>
    </>
  );
}

📝 Form

To create the form, we need to tell React what to do when the form is submitted. This can be accomplished by accessing the onSubmit event. We also need to tell React what to do when each input is changed so that we can keep track of its value. We do this by accessing the event property in the onChange event.

Let’s create a form using HTML and then access these onChange and onSubmit events using React:

// in Form.js

// function header:
export default function Form({ /*...*/ name, setName /*...*/ }) {

// in return ...
<form
	onSubmit={(e) => {     // on submission ...
		e.preventDefault();  // stop submit from reloading the page
    setShowOutput(true); // display the output,
                         // using the setShowOutput() state setter passed into this Form component
	}}
>
	<label htmlFor="member-name">Name: </label> { /* htmlFor connects the label the appropriate input field */ }
	<input
	  id="member-name" // match id to label's htmlFor
	  type="text"      // type of HTML input (text, email, phone, etc.) - see https://www.w3schools.com/html/html_form_input_types.asp
	  value={name}     // display value of the provided `name` state prop
	  onChange={(e) => { // access the onChange event handler; `e` is short for event but can be named anything */
	    setName(e.target.value); // get the value of the event (text in the input field)
	  }}
	  required // mark this HTML field as required
	/>

	{/* continue for rest of inputs */}
</form>

Continue building this out for the rest of the input fields you need. One input which will prove challenging is the photo because we need to turn the URL into a photo we can display. We do this by accessing the file’s path from the event, creating a URL from it, and then by getting an object at that URL inside of your onChange:

<input
  id="member-photo"
  type="file"
  // no `value` because value is a path, but we want to store a photo
  onChange={(event) => {
    setPhoto(URL.createObjectURL(event.target.files[0]));
  }}
  required
/>

Once this is all complete, it’s time to move on to the output section.

🏞️ Output

For the output, we need to display an image and text using the passed in properties:

export default function Output({ photo, name, title, fact }) {
  return (
    <div>
      <img src={photo ?? ""} alt="profile photo" />
      <h1>{name}</h1>
      <p>{title}</p>
      <p>Fun Fact:{fact}</p>
    </div>
  );
}
💡
Above, photo ?? "" evaluates to "" if photo is a nullish value (null or undefined). This is called the Nullish Coalescing operator.

Now we’ve got all the pieces in place!

🖌️ Touch-ups

It’s time to add some functionality details before we overhaul styling. First, we want to only display the output when showOutput is true. Add conditionality to the display of Output using a ternary operator:

{showOutput ? (<Output />) : (<></>)}
💡
If you only want to render a component/element when a given condition evaluates to true, you can alternatively use the logical AND operator (&&):
{showOutput && (<Output />)}

Next, you’ll notice that once we set showOutput to true, any changes we make are live! Let’s update the onChange methods to hide output. (This is a challenge to you. The solution is in the sample code.)

💈Styling

The final version of this project makes use of some styling. The best way to learn this is by reviewing the sample code on your own after reviewing concepts covered in

.

Both Flexbox and Grid have their own pros and cons. As you build increasingly complex layouts, you may wish (or even need) to combine both methods to achieve your design goals.

🧨 Taking This Further

In addition to styling your web app, here are some other ideas of how you could extend this based on what you’ve learned so far:

  • let people customize their card’s background color
  • support resetting the form (clearing all data)
  • add a field to differentiate mentors from general members. This could add a badge/label, change the color, or even hide the title field for non-mentors!
  • store and show a list of all profiles the user has generated
  • allow members to download an image of their profile card
  • anything else!