My personal page with React
Tags: react
Published: 2018-10-06 18:13:31

---
title: My personal page with React
published: true
description: How to create a personal page using React
tags: React
cover_image: https://thepracticaldev.s3.amazonaws.com/i/e0qb9plkzbxtjjyk97sr.png
---

I've been playing for a while with React, and my experience has been pretty satisfactory so far, such, that I decided to create my own personal page and now I want to show you how you can do it.

The whole app is on [github](https://github.com/oswaldodiaz/mypage).

## Prerequisites

First let's assume you have all your environment setup for [React](https://reactjs.org/tutorial/tutorial.html#setup-for-the-tutorial):

* Make sure you have a recent version of [Node.js](https://nodejs.org/en/) installed.
* Follow the [installation instructions for Create React App](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app) to make a new project.

I use [yarn](https://yarnpkg.com/en/) instead of npm, but is not necessary, if you rather use npm replace in commands `yarn add` for `npm install`.

```
yarn add -g create-react-app
```

## Creating a new React app

To create a new React app you just need to run `create-react-app` followed by your app's name.

``` 
create-react-app my-page
cd my-page
```

Now a basic project is created with the following structure:
![Initial code structure](https://thepracticaldev.s3.amazonaws.com/i/l945bl5k131wyvxwjr3s.png)

Let's run our app to see what we have so far.

```
yarn start
```

Your browser will open on `http://localhost:3000` and you'll see React logo spinning around.

![Initial app](https://thepracticaldev.s3.amazonaws.com/i/3pmogi9ycby0904wxo4n.png)

## Dependencies

We'll use a set of dependencies to render easily our components:

* [Bootstrap](https://reactstrap.github.io/): an open source toolkit for developing with HTML, CSS, and JS.

```
yarn add bootstrap --save
yarn add jquery --save
yarn add popper.js --save
yarn add reactstrap --save
```

* [Fontawesome](https://fontawesome.com/how-to-use/on-the-web/using-with/react): For vector icons and social logos on your website, the web’s most popular icon set and toolkit.

``` 
yarn add @fortawesome/react-fontawesome --save
yarn add @fortawesome/fontawesome-svg-core --save
yarn add @fortawesome/free-brands-svg-icons --save
```

* [Moment](https://momentjs.com/docs/): Parse, validate, manipulate, and display dates and times in JavaScript.
```
yarn add moment --save
```

Let's also import bootstrap in `index.js`

```
import 'bootstrap/dist/css/bootstrap.min.css';
```

## Run App
Maintain the app running so you can see your change on real time.

```
yarn start
```

## Components

The magic of React is to handle fragments of your page as **components**, where each component works independently, that way the code can be *replace, relocate or refactor* easily without the need of spike the whole app. For this project we will have 3 main components: **top bar**, **profile** and **footer** where each component has a specific subject to render, and each of them can later be divide in other components.

Let's add a folder called *components* to our src, and let's create our 3 components:

```
mkdir src/components
touch src/components/TopBar.js
touch src/components/Profile.js
touch src/components/Footer.js
```

The following will be the code of component `TopBar.js`.
```javascript
import React from 'react';

class TopBar extends React.Component {
  render() {
    return <div>This is the top bar</div>;
  }
}

export default TopBar;
```

First, we're creating a component called TopBar that extends from React Component, this class has a unique method called render() and it needs to return an HTML tag, in this case a simple `<div>This is the top bar</div>`. At the end we need to export this class so we can use it in others components. Replicate the same code for `Profile.js` and `Footer.js`. 

Now, let's modify `App.js` so it renders our new components:

```javascript
import React, { Component } from 'react';
import './App.css';
import TopBar from './components/TopBar';
import Profile from './components/Profile';
import Footer from './components/Footer';

class App extends Component {
  render() {
    return (
      <div className="App">
        <TopBar />
        <Profile />
        <Footer />
      </div>
    );
  }
}

export default App;
```

Now we're able to see the following:

![Initial page with components](https://thepracticaldev.s3.amazonaws.com/i/a3rcgu9ng5ihmd5xbs01.png)

### Top Bar

Top bar will consist of a [navigation bar](https://reactstrap.github.io/components/navbar/) with the user's name and 1 link to **Profile**. Let's replace our code on `TopBar.js`.

```javascript
import React from 'react';
import {Container, Collapse, Navbar, NavbarToggler, NavbarBrand, Nav, NavItem, NavLink } from 'reactstrap';

class TopBar extends React.Component {
  constructor(props) {
    super(props);

    this.toggle = this.toggle.bind(this);
    this.state = {
      isOpen: false
    };
  }

  toggle() {
    this.setState({
      isOpen: !this.state.isOpen
    });
  }

  render() {
    return (
      <Navbar color="dark" dark expand="md">
        <Container>
          <NavbarBrand href="/">
            <span>Oswaldo Díaz</span>
          </NavbarBrand>
          <NavbarToggler onClick={this.toggle}/>
          <Collapse isOpen={this.state.isOpen} navbar>
            <Nav className="ml-auto" navbar>
              <NavItem>
                <NavLink href="/profile/">Profile</NavLink>
              </NavItem>
            </Nav>
          </Collapse>
        </Container>
      </Navbar>
    );
  }
}

export default TopBar;
```

Now our design has a proper top bar.

![App with top bar](https://thepracticaldev.s3.amazonaws.com/i/1mvxilmn8no4fnqsbk1v.png)


### Footer
For the footer we will use [FontAwesome](https://fontawesome.com/how-to-use/on-the-web/using-with/react) to render [Github](https://github.com/) and [Linkedin](https://linkedin.com/) icons with links to each profile. Now let's replace our `Footer.js` content.

```javascript
import React from 'react';
import {Container} from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faLinkedin, faGithub } from '@fortawesome/free-brands-svg-icons'

class Footer extends React.Component {
  render() {
    return (
      <footer>
        <hr/>
        <Container>
          <a href="https://www.linkedin.com/in/oswaldo-d%C3%ADaz-397b036b/"><FontAwesomeIcon icon={faLinkedin} /></a>
          <a href="https://github.com/oswaldodiaz"><FontAwesomeIcon icon={faGithub}  /></a>
        </Container>
      </footer>
    )
  }
}

export default Footer;
```

With the code above, 2 icons will appear at the end with links to our linkedin and github profiles.

![App with footer](https://thepracticaldev.s3.amazonaws.com/i/ix4m3469qhuqo8iivfcz.png)

### Profile

For the profile let's divide content in 2 sections, first a summary of you and then a [tab](https://reactstrap.github.io/components/tabs/) with more detailed information. The second section will render new components `Experience.js` and `Education.js`.

Before we move on, let's create a JSON file where we can store all or professional experience and then we will render this information using proper components:


```
touch src/profile.json
```

And add the following content to `profile.json`.

```json
{
  "title": "Software Engineer",
  "summary": "I'm passionate about Computer Software development, looking always for different and most efficient solutions to daily problems of clients. Always growing as a person and as a co worker, looking always to give my best for the company I'm working with.",
  "studies": [
    {
      "institute": "Universidad Central de Venezuela",
      "logo": "https://media.licdn.com/dms/image/C560BAQEqH4pTb_C3Vg/company-logo_400_400/0?e=1547078400&v=beta&t=w7dqsi-UJEkF4ChiuDRTSUPQ0H_hZiJ6NwWvix_b1Uc",
      "url": "http://www.ciens.ucv.ve/",
      "title": "Bachelor on Computer Science",
      "durationInYears": "5",
      "graduationYear": 2013
    }
  ],
  "certifications": [
    {
      "logo": "https://www.certmetrics.com/api/ob/image/amazon/c/1",
      "url": "https://aws.amazon.com/certification/certified-solutions-architect-associate/",
      "title": "AWS Certified Solutions Architect - Associate",
      "issuer": "Amazon Web Services",
      "issueDate": "2018-09-01",
      "expiryDate": "2020-09-01",
      "verificationLink": "https://www.certmetrics.com/amazon/public/badge.aspx?i=1&t=c&d=2018-09-13&ci=AWS00487431"
    },
    {
      "logo": "https://acclaim-production-app.s3.amazonaws.com/images/86629924-6c10-442c-8742-05ff5e45e922/Oracle-Certification-badge_OC-Associate.png",
      "url": "https://education.oracle.com/es/oracle-certified-associate-java-se-8-programmer/trackp_333",
      "title": "Oracle Certified Associate, Java SE 8 Programmer",
      "issuer": "Oracle",
      "issueDate": "2016-05-01",
      "verificationLink": "https://www.youracclaim.com/badges/aa466aec-ddbc-4e67-8038-aa8466a4aef9/linked_in_profile"
    }
  ],
  "experiences": [
    {
      "companyName": "ServiceRocket",
      "logo": "https://media.licdn.com/dms/image/C560BAQE0UGAq3qc4PA/company-logo_200_200/0?e=1547078400&v=beta&t=wPEtCDSDDI8HHn779fD3yG5tr95YQC6pe71f81HU7oQ",
      "url": "https://www.servicerocket.com/",
      "roles": [
        {
          "title": "Software developer",
          "description": "Development of Application (bots, add-ons) that help simplify day-to-day work for companies using platforms such as Workplace or Atlassian products. To achieve this purpose, Serverless framework is being used taking advantage of AWS stack technologies.",
          "startDate": "2018-01-01",
          "currentJob": true,
          "location": "Santiago Province, Chile"
        },
        {
          "title": "Agile Java Developer",
          "description": "Own the processes of squad, develop code, develop user stories, estimates and other attachments, collaboratively with Product Owner through formal and informal meetings, manage the risks of committed backlog items, manage the quality of processes, output, and throughput.",
          "startDate": "2016-07-01",
          "endDate": "2018-01-01",
          "location": "Santiago Province, Chile"
        }
      ]
    },
    {
      "companyName": "Alaya Digital Solutions",
      "logo": "https://media.licdn.com/dms/image/C4D0BAQEOGmtbMe-jiA/company-logo_400_400/0?e=1547078400&v=beta&t=O-BNIwr7tSojDadQq7WHlBT349-M2WEl7tgY4IJxLzU",
      "url": "http://www.alaya.cl/",
      "roles": [
        {
          "title": "Project Lead",
          "description": "In charge of planning, take decissions about the technology to used and manage projects with a software development team with abilities to build web applications. This job position requires to have knowledges of the technology used such as Java 8, Spring 4, Hibernate, J2EE, Oracle DB, Weblogic and be able to write the code if neccessary.",
          "startDate": "2016-03-01",
          "endDate": "2016-07-01",
          "location": "Santiago Province, Chile"
        },
        {
          "title": "Software Engineer",
          "description": "In charge of building applications for public entities working with backend (J2EE, Srping, Hibernate, OracleDB) and frontend (ExtJs) through REST web services. Responsible of applying good design patterns allowing the increment of software easier and faster on each iteration.",
          "startDate": "2015-10-01",
          "endDate": "2016-03-01",
          "location": "Santiago Province, Chile"
        }
      ]
    },
    {
      "companyName": "Synergy-GB",
      "logo": "https://media.licdn.com/dms/image/C4E0BAQG8mmQH36m-Xg/company-logo_400_400/0?e=1547078400&v=beta&t=ijgqvtMLx6iNypHtW_7WySNju7rTP7Tvo3ujAchGvck",
      "url": "http://www.alaya.cl/",
      "roles": [
        {
          "title": "Project Lead",
          "description": "Project engineer with interest on design and develop of software components responsible for managing the logic and systems integration, that allow communicate web and mobile applications with the data sources of a business. With experiences developing REST/SOAP web services in Java and design and modeling of data for SQL and No-SQL databases.",
          "startDate": "2014-04-01",
          "endDate": "2015-10-01",
          "location": "Caracas, Venezuela"
        }
      ]
    },
    {
      "companyName": "IBM",
      "logo": "https://media.licdn.com/dms/image/C560BAQEZL5_LD7kuhg/company-logo_400_400/0?e=1547078400&v=beta&t=kD5vayFgrDcbzzpbNYhpfPPF0kr-pQWAnuHdl0j7mRU",
      "url": "https://www.ibm.com/ve-es/",
      "roles": [
        {
          "title": "Sap Consultant",
          "description": "ABAP and Portal Consultant with interest of developing ABAP programs that can be use at a high level for other departments and also with abilities to install and migrate a Portal SAP system.",
          "startDate": "2013-06-01",
          "endDate": "2014-03-01",
          "location": "Caracas, Venezuela"
        }
      ]
    },
    {
      "companyName": "4Geeks",
      "logo": "https://media.licdn.com/dms/image/C4D0BAQERvzTMXtxd7g/company-logo_400_400/0?e=1547078400&v=beta&t=I_dafILp03Xn8WaI9-9IBHah7Z5wPpW55D8WY1kgR6c",
      "url": "https://www.4geeksacademy.co",
      "roles": [
        {
          "title": "Web programmer",
          "description": "In charge of developing web services using the framework Django used for a e-commerce web site",
          "startDate": "2012-11-01",
          "endDate": "2013-03-01",
          "location": "Caracas, Venezuela"
        }
      ]
    }
  ]
}
```

File `profile.json` has general information (**title** and **summary**) and sections **studies**, **certification** and **experience** that we'll render later using components. 


Let's create 2 new components:
* `Experience.js`: to render job experience.
* `Education.js`: to render education (studies and certifications).

```
touch src/components/Experience.js
touch src/components/Education.js
```

First, for `Experience.js` component let's use [Media](https://reactstrap.github.io/components/media/) to render our job experience.

```javascript
import React from "react";
import { Container, Row, Col } from "reactstrap";
import profile from "../profile";
import moment from "moment";
import { Media } from "reactstrap";

function getDuration(duration) {
  const years = parseInt(duration / 12);
  const months = (duration > 12)? duration % 12 : duration
  return (years > 0? years + " year" + (years > 1? "s": "") + " and " : "") + (months > 0? months + " month" + (months > 1? "s": "") : "");
};

class Experience extends React.Component {
  render() {
    return <Container>
      <Row>
        <Col>
          {profile.experiences.map(function (experience, i) {
            moment.locale('en');

            const totalDuration = experience.roles.reduce(function (cnt, role) {
              const startDate = moment(role.startDate);
              const timeEnd = moment(role.currentJob ? new Date() : new Date(role.endDate));
              const duration = moment.duration(timeEnd.diff(startDate));
              return Number(cnt) + Number(duration.asMonths().toPrecision(1));
            }, 0);

            return (
              <div key={i}>
                <Media>
                  <Media left top href={experience.url}>
                    <Media object src={experience.logo} alt={experience.companyName}/>
                  </Media>
                  <Media body>
                    <Media heading>
                      <a href={experience.url}>{experience.companyName}</a>
                      <span className="jobTotalDuration">{getDuration(totalDuration)}</span>
                    </Media>

                    {experience.roles.map(function (role, i) {
                      const startDate = moment(role.startDate);
                      const timeEnd = moment(role.currentJob ? new Date() : new Date(role.endDate));
                      const duration = Number(moment.duration(timeEnd.diff(startDate)).asMonths().toPrecision(1));

                      return <div key={i}>
                        <h5>{role.title}</h5>
                        <span
                          className="jobDuration">{startDate.format('MMM YYYY')} - {role.currentJob ? 'Present' : timeEnd.format('MMM YYYY')} ({getDuration(duration)})</span>
                        <span className="jobLocation">{role.location}</span>
                        <p className="jobDescription">{role.description}</p>
                      </div>
                    })}
                  </Media>
                </Media>
              </div>
            );
          })}
        </Col>
      </Row>
    </Container>
  }
}

export default Experience;
```

We're importing the JSON we created some steps above through: `import profile from "../profile";`.

In this component we're defining a **function** that it's been used to clean up the code and abstract that logic from the component.

Another thing we're adding is **map** to iterate over a collection of experience and on each experience (and over a collection of roles as well).

Now, let's work on `Education.js`. We'll be using Media as well.

```javascript
import React from 'react';
import {Container, Row, Col, Media} from 'reactstrap';
import moment from 'moment';
import profile from '../profile.json';

class Education extends React.Component {
  render() {
    return <Container>
      <Row>
        <Col>
          {profile.studies.map(function (study, i) {
            return <Media key={i}>
              <Media left top href={study.url}>
                <Media object src={study.logo} alt={study.institute}/>
              </Media>
              <Media body>
                <Media heading>
                  <a href={study.url}>{study.institute}</a>
                </Media>
                {
                  [
                    {
                      "key": "Title",
                      "value": study.title
                    },
                    {
                      "key": "Graduation Year",
                      "value": study.graduationYear
                    },
                    {
                      "key": "Duration",
                      "value": study.durationInYears + " year(s)"
                    }
                  ].map(function (object, i) {
                    return <div>
                      <Row>
                        <Col className="formLabel">{object.key}:</Col>
                      </Row>
                      <Row>
                        <Col>{object.value}</Col>
                      </Row>
                    </div>
                  })
                }
              </Media>
            </Media>
          })}
        </Col>
      </Row>
      <br/>
      <br/>
      <Row>
        <Col>
          <h4>Certifications:</h4>
          <hr/>
          {profile.certifications.map(function (certification, i) {
            const verification = certification.verificationLink ?
              <Row>
                <Col>
                  <a className="certificateLink" href={certification.verificationLink}>See certificate</a>
                </Col>
              </Row> : "";
            return <Media key={i}>
              <Media left top href={certification.url}>
                <Media object src={certification.logo} alt={certification.title}/>
              </Media>
              <Media body>
                <Media heading>
                  <a href={certification.url}>{certification.title}</a>
                </Media>
                <Row>
                  <Col>{moment(certification.issueDate).format('MMM YYYY')} - {(certification.expiryDate) ? moment(certification.expiryDate).format('MMM YYYY') : 'Present'}</Col>
                </Row>
                <Row>
                  <Col>{certification.issuer}</Col>
                </Row>
                {verification}
              </Media>
            </Media>
          })}
        </Col>
      </Row>
    </Container>
  }
}

export default Education;
```

Now that we have our 2 sections defined, let's modify `Profile.js`.

```javascript
import React from 'react';
import {Jumbotron, Container, TabContent, TabPane, Nav, NavItem, NavLink } from "reactstrap";
import classnames from 'classnames';
import Experience from "./Experience";
import Education from './Education'
import profile from '../profile.json'

class Profile extends React.Component {
  constructor(props) {
    super(props);

    this.toggle = this.toggle.bind(this);
    this.state = {
      activeTab: '1'
    };
  }

  toggle(tab) {
    if (this.state.activeTab !== tab) {
      this.setState({
        activeTab: tab
      });
    }
  }

  render() {
    return <div>
      <Jumbotron>
        <Container>
          <h1 className="display-3">{profile.title}</h1>
          <p className="lead">{profile.summary}</p>
        </Container>
      </Jumbotron>

      <Container>
        <Nav tabs>
          <NavItem>
            <NavLink className={classnames({ active: this.state.activeTab === '1' })}
                     onClick={() => { this.toggle('1'); }}>
              Experience
            </NavLink>
          </NavItem>
          <NavItem>
            <NavLink className={classnames({ active: this.state.activeTab === '2' })}
                     onClick={() => { this.toggle('2'); }}>
              Education
            </NavLink>
          </NavItem>
        </Nav>
        <TabContent activeTab={this.state.activeTab}>
          <TabPane tabId="1">
            <Experience/>
          </TabPane>
          <TabPane tabId="2">
            <Education/>
          </TabPane>
        </TabContent>
      </Container>
    </div>;
  }
}

export default Profile;
```

First, we're using a [Jumbotron](https://reactstrap.github.io/components/jumbotron/) for the **summary** and [Tabs](https://reactstrap.github.io/components/tabs/) for **Experience** and **Education**.



### Addding a profile picture


Look for your profile pic and save it under `src` folder with name `profilePic.jpg` (it could be another name).

Let's modify `TopBar.js` to add a profile pic:
```
...
import profilePic from '../profilePic.jpg';

class TopBar extends React.Component {
  ...

  render() {
    return (
      <Navbar color="dark" dark expand="md">
        <Container>
          <NavbarBrand href="/">
            <img src={profilePic} className="profile-pic" alt="Oswaldo Díaz"/>
            ...
```

### Adding style

Finally, let's add some style by modify `App.css`.

```css
.profile-pic {
  height: 4vmin;
  margin-right: 10px;
  border-radius: 50px;
}

a {
  color: #343a40;
}

a:hover {
  color: #173798;
  text-decoration: none;
}

div.media {
  margin-top: 30px;
  margin-bottom: 10px;
}

div.media .media-heading .jobTotalDuration {
  color: #666;
  font-size: 14px;
  display: block;
}

div.media img {
  height: 10vmin;
  margin-right: 10px;
  border-radius: 50px;
}

div.media .jobDuration {
  color: #444;
  display: block;
}

div.media .jobLocation {
  color: #666;
}

.formLabel {
  font-weight: bold;
}

.certificateLink {
  color: cornflowerblue;
}

footer {
  margin-top: 20px;
  margin-bottom: 10px;
  border-top: #444444;
  text-align: center;
}

footer a {
  color: black;
  font-size: 30px;
  margin-right: 10px;
  margin-left: 10px;
}
```

### Final result

**Experience**
![Experience Tab](https://thepracticaldev.s3.amazonaws.com/i/x73ts8ag33m4wl9crhfa.png)

**Education**
![Education Tab](https://thepracticaldev.s3.amazonaws.com/i/avr1x9r6y4s1h911f3b0.png)

This is my current page hosted in AWS as a S3 static website under url http://oswaldodiaz.info.

Let me know if you find this tutorial useful, if something was not clear enough or if you have experience with React what would you do different ;)