Artigo original: https://www.freecodecamp.org/news/build-portfolio-website-react/

Hoje, você criará uma das aplicações mais importantes que você pode criar para si mesmo: seu portfólio de desenvolvedor.

Todo desenvolvedor React – ou desenvolvedor para a web, em geral – precisa ser capaz de mostrar o que pode fazer a qualquer cliente ou empregador em potencial.

É exatamente isso que vamos criar agora, com a ajuda de várias ferramentas padrão do setor, incluindo o React, o Tailwind CSS e o Netlify.

Vamos começar!

Como será o portfólio?

portfolio-1-min

Essa é a versão final do portfólio que você vai construir.

Ela apresentará informações sobre você, quais projetos você fez, quais habilidades você usou para fazer esses projetos e incluirá um formulário de contato para clientes ou empregadores entrarem em contato com você.

Quais ferramentas usaremos?

  • Usaremos o React para criar a interface do usuário da aplicação. Isso nos permitirá compor cada parte de nossa página de destino por meio de componentes reutilizáveis e estender nossa aplicação se quisermos adicionar recursos adicionais, como um blog.
  • Para estilizar nossa aplicação, usaremos o Tailwind CSS. Para dar a ela uma aparência profissional, o Tailwind nos permitirá aplicar facilmente vários estilos através da combinação de nomes de classe em nossos elementos do React.
  • Para enviar nossa aplicação para a web, usaremos o serviço gratuito do Netlify. Ele servirá nosso projeto em um domínio personalizado para os usuários muito rapidamente com a ajuda de uma CDN (rede de entrega de conteúdo, ou content delivery network, em inglês).

Começando os trabalhos

Você pode fazer o download dos arquivos iniciais do projeto aqui.

Quando você obtiver os arquivos do código, tudo o que terá que fazer é arrastar sua pasta de projeto (descompactada) para o editor de código e executar o comando:

npm install

É isso! Podemos começar.

De quais ferramentas eu precisarei para criar o portfólio?

Para passar por todo o processo de criação da nossa aplicação, do início à implantação, você precisará ter o seguinte:

  1. Node.js instalado no computador. Você pode baixá-lo em nodejs.org.
  2. Git instalado no seu computador. Você pode baixá-lo em git-scm.com.
  3. Eu recomendaria usar o VS Code como seu editor de código. Você pode baixá-lo em code.visualstudio.com.
  4. Uma conta gratuita da Netlify em netlify.com.
  5. Uma conta gratuita do GitHub em github.com.

Como criar a estrutura do portfólio

O benefício de se usar o React é que podemos expandir nossa aplicação, tendo quantas páginas quisermos, de modo muito simples, e adicionar muito conteúdo adicional.

No entanto, como estamos trabalhando apenas com uma página, podemos, dentro do nosso componente da aplicação, descobrir os diferentes componentes de que precisamos muito rapidamente. Teremos uma barra de navegação no topo com todos os links para saltar para diferentes seções do nosso portfólio.

Depois disso, incluiremos uma seção About (sobre o desenvolvedor – você), uma seção para nossos projetos, depoimentos e, finalmente, nosso formulário de contato.

Esse planejamento rápido nos permite descobrir quais dos nossos componentes devem ser nomeados e em que ordem. Podemos, agora, adicionar todos eles ao nosso arquivo App.js (em src):

// src/App.js

import React from "react";

export default function App() {
  return (
    <main>
      <Navbar />
      <About />
      <Projects />
      <Skills />
      <Testimonials />
      <Contact />
    </main>
  );
}

Como criar nossos componentes

Agora que temos todos esses componentes listados, precisamos criá-los.

Dentro da nossa pasta source (src), vamos criar uma pasta chamada components com todos os arquivos que precisamos:

my-portfolio
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   └── manifest.json
└── src
    ├── App.js
    ├── data.js
    ├── index.css
    ├── index.js
    └── components
        ├── About.js
        ├── Contact.js
        ├── Navbar.js
        ├── Projects.js
        ├── Skills.js
        └── Testimonials.js

Em seguida, criaremos a estrutura básica de cada componente do React e exportaremos a partir desse arquivo com export default:

// src/components/About.js

export default function About() {}

// repita a mesma estrutura básica para todos os 6 componentes

Por fim, importamos de volta para o App.js:

// src/App.js

import React from "react";
import About from "./components/About";
import Contact from "./components/Contact";
import Navbar from "./components/Navbar";
import Projects from "./components/Projects";
import Skills from "./components/Skills";
import Testimonials from "./components/Testimonials";

export default function App() {
  return (
    <main>
      <Navbar />
      <About />
      <Projects />
      <Skills />
      <Testimonials />
      <Contact />
    </main>
  );
}

Observe que precisamos ter seis componentes, no total.

Introdução ao Tailwind CSS

Feito isso, podemos começar a trabalhar com o Tailwind CSS, a fim de começar a dar à nossa aplicação uma aparência básica.

A vantagem de usar o Tailwind CSS é que não precisamos escrever nenhum estilo manualmente em uma folha de estilo do CSS. Tudo o que temos que fazer é combinar várias classes para criar a aparência que queremos.

Por exemplo, para dar ao nosso portfólio um fundo escuro com texto cinza aplicado a todos os nossos componentes filho, você pode adicionar as seguintes classes ao nosso elemento main:

// src/App.js

import React from "react";
import About from "./components/About";
import Contact from "./components/Contact";
import Navbar from "./components/Navbar";
import Projects from "./components/Projects";
import Skills from "./components/Skills";
import Testimonials from "./components/Testimonials";

export default function App() {
  return (
    <main className="text-gray-400 bg-gray-900 body-font">
      <Navbar />
      <About />
      <Projects />
      <Skills />
      <Testimonials />
      <Contact />
    </main>
  );
}

Como criar o componente About

Vamos começar em nossa primeira seção, a seção About. Ela consistirá em uma introdução básica a nós mesmos e em quais habilidades nos especializamos.

Ela também incluirá alguns links para o formulário de contato, bem como para nossos projetos anteriores. Como esses links serão para diferentes partes de uma mesma página, podemos usar as hashes: "/#projects" e "/#contact".

Para fazer com que esses links funcionem e para poder saltar para cada seção, definiremos o atributo id da seção de projetos como "projects" e os da seção de contato como "contact".

// src/components/About.js

import React from "react";

export default function About() {
  return (
    <section id="about">
      <div className="container mx-auto flex px-10 py-20 md:flex-row flex-col items-center">
        <div className="lg:flex-grow md:w-1/2 lg:pr-24 md:pr-16 flex flex-col md:items-start md:text-left mb-16 md:mb-0 items-center text-center">
          <h1 className="title-font sm:text-4xl text-3xl mb-4 font-medium text-white">
            Hi, I'm Reed.
            <br className="hidden lg:inline-block" />I love to build amazing
            apps.
          </h1>
          <p className="mb-8 leading-relaxed">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui
            laborum quasi, incidunt dolore iste nostrum cupiditate voluptas?
            Laborum, voluptas natus?
          </p>
          <div className="flex justify-center">
            <a
              href="#contact"
              className="inline-flex text-white bg-green-500 border-0 py-2 px-6 focus:outline-none hover:bg-green-600 rounded text-lg">
              Work With Me
            </a>
            <a
              href="#projects"
              className="ml-4 inline-flex text-gray-400 bg-gray-800 border-0 py-2 px-6 focus:outline-none hover:bg-gray-700 hover:text-white rounded text-lg">
              See My Past Work
            </a>
          </div>
        </div>
        <div className="lg:max-w-lg lg:w-full md:w-1/2 w-5/6">
          <img
            className="object-cover object-center rounded"
            alt="hero"
            src="./coding.svg"
          />
        </div>
      </div>
    </section>
  );
}

Para a imagem no lado direito da seção, estou usando um arquivo svg da pasta public, chamado coding.svg.

Essa imagem serve apenas como um espaço reservado temporário. Eu recomendo usar uma imagem real de si mesmo.

Como criar o componente Projects

Nossa seção de projetos consistirá em um elemento section com o id "projects". Ele contará com uma galeria de todos os projetos que criamos, o que incluirá imagens.

Ele terá o título do projeto, juntamente com as tecnologias que usamos para fazê-lo, bem como um link para ele (se ele estiver na web).

// src/components/Projects.js

import { CodeIcon } from "@heroicons/react/solid";
import React from "react";
import { projects } from "../data";

export default function Projects() {
  return (
    <section id="projects" className="text-gray-400 bg-gray-900 body-font">
      <div className="container px-5 py-10 mx-auto text-center lg:px-40">
        <div className="flex flex-col w-full mb-20">
          <CodeIcon className="mx-auto inline-block w-10 mb-4" />
          <h1 className="sm:text-4xl text-3xl font-medium title-font mb-4 text-white">
            Apps I've Built
          </h1>
          <p className="lg:w-2/3 mx-auto leading-relaxed text-base">
            Lorem ipsum, dolor sit amet consectetur adipisicing elit. Explicabo
            facilis repellat ab cupiditate alias vero aliquid obcaecati quisquam
            fuga dolore.
          </p>
        </div>
        <div className="flex flex-wrap -m-4">
          {projects.map((project) => (
            <a
              href={project.link}
              key={project.image}
              className="sm:w-1/2 w-100 p-4">
              <div className="flex relative">
                <img
                  alt="gallery"
                  className="absolute inset-0 w-full h-full object-cover object-center"
                  src={project.image}
                />
                <div className="px-8 py-10 relative z-10 w-full border-4 border-gray-800 bg-gray-900 opacity-0 hover:opacity-100">
                  <h2 className="tracking-widest text-sm title-font font-medium text-green-400 mb-1">
                    {project.subtitle}
                  </h2>
                  <h1 className="title-font text-lg font-medium text-white mb-3">
                    {project.title}
                  </h1>
                  <p className="leading-relaxed">{project.description}</p>
                </div>
              </div>
            </a>
          ))}
        </div>
      </div>
    </section>
  );
}

Observe que também vamos usar a biblioteca @heroicons/react para poder criar alguns ícones em SVG como componentes do React.

Estamos importando um array projects de um arquivo data.js da mesma pasta. Lá, exportamos um array de objetos, onde cada objeto inclui os dados de um projeto individual:

// src/data.js

export const projects = [
  {
    title: "React Reserve",
    subtitle: "MERN Stack",
    description:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?",
    image: "./project-1.gif",
    link: "https://reactbootcamp.com",
  },
  {
    title: "React Tracks",
    subtitle: "React and Python",
    description:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?",
    image: "./project-2.gif",
    link: "https://reedbarger.com",
  },
  {
    title: "DevChat",
    subtitle: "React and Firebase",
    description:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?",
    image: "./project-3.gif",
    link: "https://jsbootcamp.com",
  },
  {
    title: "Epic Todo App",
    subtitle: "React Hooks",
    description:
      "Lorem ipsum dolor sit amet consectetur adipisicing elit. Praesentium dolore rerum laborum iure enim sint nemo omnis voluptate exercitationem eius?",
    image: "./project-4.gif",
    link: "https://pythonbootcamp.com",
  },
];

Como criar o componente Skills

Vamos preencher a seção para todas as habilidades e tecnologias que conhecemos.

Isso consistirá em uma lista simples de todas as principais ferramentas com as quais estamos familiarizados e podemos usar em nossos projetos para empregadores ou clientes.

Mais uma vez, vamos importar um array de nossa pasta de dados. Esse array, contudo, consiste em várias strings que representam cada uma das habilidades que conhecemos, como JavaScript, React e Node:

// src/components/Skills.js

import { BadgeCheckIcon, ChipIcon } from "@heroicons/react/solid";
import React from "react";
import { skills } from "../data";

export default function Skills() {
  return (
    <section id="skills">
      <div className="container px-5 py-10 mx-auto">
        <div className="text-center mb-20">
          <ChipIcon className="w-10 inline-block mb-4" />
          <h1 className="sm:text-4xl text-3xl font-medium title-font text-white mb-4">
            Skills &amp; Technologies
          </h1>
          <p className="text-base leading-relaxed xl:w-2/4 lg:w-3/4 mx-auto">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nisi sit
            ipsa delectus eum quo voluptas aspernatur accusantium distinctio
            possimus est.
          </p>
        </div>
        <div className="flex flex-wrap lg:w-4/5 sm:mx-auto sm:mb-2 -mx-2">
          {skills.map((skill) => (
            <div key={skill} className="p-2 sm:w-1/2 w-full">
              <div className="bg-gray-800 rounded flex p-4 h-full items-center">
                <BadgeCheckIcon className="text-green-400 w-6 h-6 flex-shrink-0 mr-4" />
                <span className="title-font font-medium text-white">
                  {skill}
                </span>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

Como criar o componente Testimonials

No componente Testimonials (em português, depoimentos), vamos listar alguns depoimentos – de clientes anteriores, quem sabe, ou de pessoas que estão familiarizadas com o nosso trabalho.

Eles consistirão em alguns cartões que apresentam o depoimento em si, bem como de quem ele é e a empresa onde essa pessoa trabalha.

Também estamos importando um array testimonials com vários objetos que apresentam o depoimento em si, a imagem, o nome da pessoa e a empresa.

// src/components/Testimonials

import React from "react";
import { TerminalIcon, UsersIcon } from "@heroicons/react/solid";
import { testimonials } from "../data";

export default function Testimonials() {
  return (
    <section id="testimonials">
      <div className="container px-5 py-10 mx-auto text-center">
        <UsersIcon className="w-10 inline-block mb-4" />
        <h1 className="sm:text-4xl text-3xl font-medium title-font text-white mb-12">
          Client Testimonials
        </h1>
        <div className="flex flex-wrap m-4">
          {testimonials.map((testimonial) => (
            <div className="p-4 md:w-1/2 w-full">
              <div className="h-full bg-gray-800 bg-opacity-40 p-8 rounded">
                <TerminalIcon className="block w-8 text-gray-500 mb-4" />
                <p className="leading-relaxed mb-6">{testimonial.quote}</p>
                <div className="inline-flex items-center">
                  <img
                    alt="testimonial"
                    src={testimonial.image}
                    className="w-12 rounded-full flex-shrink-0 object-cover object-center"
                  />
                  <span className="flex-grow flex flex-col pl-4">
                    <span className="title-font font-medium text-white">
                      {testimonial.name}
                    </span>
                    <span className="text-gray-500 text-sm uppercase">
                      {testimonial.company}
                    </span>
                  </span>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

Como criar o componente Contact

No final da nossa página de destino, incluiremos nosso formulário de contato para permitir que potenciais empregadores entrem em contato conosco.

Esse formulário terá 3 entradas: um nome, um e-mail e a mensagem.

Para receber esses envios de formulários, usaremos a ferramenta Netlify Forms, que cuidará facilmente de salvar essas mensagens.

// src/components/Contact.js

import React from "react";

export default function Contact() {
  return (
    <section id="contact" className="relative">
      <div className="container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap">
        <div className="lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative">
          <iframe
            width="100%"
            height="100%"
            title="map"
            className="absolute inset-0"
            frameBorder={0}
            marginHeight={0}
            marginWidth={0}
            style={{ filter: "opacity(0.7)" }}
            src="https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"
          />
          <div className="bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md">
            <div className="lg:w-1/2 px-6">
              <h2 className="title-font font-semibold text-white tracking-widest text-xs">
                ADDRESS
              </h2>
              <p className="mt-1">
                97 Warren St. <br />
                New York, NY 10007
              </p>
            </div>
            <div className="lg:w-1/2 px-6 mt-4 lg:mt-0">
              <h2 className="title-font font-semibold text-white tracking-widest text-xs">
                EMAIL
              </h2>
              <a className="text-indigo-400 leading-relaxed">
                reedbarger@email.com
              </a>
              <h2 className="title-font font-semibold text-white tracking-widest text-xs mt-4">
                PHONE
              </h2>
              <p className="leading-relaxed">123-456-7890</p>
            </div>
          </div>
        </div>
        <form
          netlify
          name="contact"
          className="lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0">
          <h2 className="text-white sm:text-4xl text-3xl mb-1 font-medium title-font">
            Hire Me
          </h2>
          <p className="leading-relaxed mb-5">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          </p>
          <div className="relative mb-4">
            <label htmlFor="name" className="leading-7 text-sm text-gray-400">
              Name
            </label>
            <input
              type="text"
              id="name"
              name="name"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
            />
          </div>
          <div className="relative mb-4">
            <label htmlFor="email" className="leading-7 text-sm text-gray-400">
              Email
            </label>
            <input
              type="email"
              id="email"
              name="email"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
            />
          </div>
          <div className="relative mb-4">
            <label
              htmlFor="message"
              className="leading-7 text-sm text-gray-400">
              Message
            </label>
            <textarea
              id="message"
              name="message"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"
            />
          </div>
          <button
            type="submit"
            className="text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg">
            Submit
          </button>
        </form>
      </div>
    </section>
  );
}

Como incluir uma localização do Google Maps

À esquerda do formulário, incluiremos um mapa do Google Maps incorporado do Google, mostrando a nossa localização.

Podemos fazê-lo com a ajuda de uma ferramenta on-line chamada embed-map.com. Tudo o que você precisa fazer é digitar sua localização e clicar em "Gerar código HTML".

No código que nos é dado, não copie todo o código, apenas o atributo src do elemento iframe. Substituiremos esse valor pelo valor src padrão que temos para o nosso iframe.

portfolio-2

Para enviar quaisquer dados de formulário para o Netlify, o Netlify Forms precisa reconhecer um formulário como HTML estático. Como nossa aplicação do React é controlada por JavaScript e não consiste em HTML simples, precisamos adicionar um formulário oculto ao nosso arquivo de index.html na pasta public.

<!-- public/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- head content skipped -->
  </head>
  <body>

  <form name="contact" netlify netlify-honeypot="bot-field" hidden>
    <input type="text" name="name" />
    <input type="email" name="email" />
    <textarea name="message"></textarea>
  </form>
  
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>

Precisamos esconder esse formulário, pois ele não precisa ser visto pelo usuário, apenas pelo Netlify.

Daremos a ele o atributo hidden, bem como um atributo name que corresponda ao formulário de JSX em Contact.js. Também precisamos dar a ele o atributo netlify para que o Netlify Forms o reconheça. Finalmente, precisamos incluir todas as mesmas entradas de nosso formulário JSX: nome, e-mail e mensagem.

Como enviar o formulário de contato

Feito isso, voltaremos para o Contact.js. Vamos usar o JavaScript para enviar esse formulário.

Em primeiro lugar, vamos criar algum state dedicado para cada um dos valores digitados no formulário – nome, e-mail e mensagem:

const [name, setName] = React.useState("");
const [email, setEmail] = React.useState("");
const [message, setMessage] = React.useState("");

Armazenaremos o que o usuário digita em cada uma das entradas no state com a ajuda do manipulador onChange.

Para lidar com o envio do formulário, adicionaremos a prop onSubmit a ele. A função que será chamada, handleSubmit, fará uma solicitação de POST para o endpoint "/" com todos os dados do nosso formulário.

Definiremos os cabeçalhos da solicitação para indicar que estamos enviando dados de formulário. Para o corpo da solicitação, incluiremos o nome do formulário, bem como todos os dados das variáveis do state do formulário – name, email e message.

// src/components/Contact.js

import React from "react";

export default function Contact() {
  const [name, setName] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [message, setMessage] = React.useState("");

  function encode(data) {
    return Object.keys(data)
      .map(
        (key) => encodeURIComponent(key) + "=" + encodeURIComponent(data[key])
      )
      .join("&");
  }

  function handleSubmit(e) {
    e.preventDefault();
    fetch("/", {
      method: "POST",
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      body: encode({ "form-name": "contact", name, email, message }),
    })
      .then(() => alert("Message sent!"))
      .catch((error) => alert(error));
  }

  return (
    <section id="contact" className="relative">
      <div className="container px-5 py-10 mx-auto flex sm:flex-nowrap flex-wrap">
        <div className="lg:w-2/3 md:w-1/2 bg-gray-900 rounded-lg overflow-hidden sm:mr-10 p-10 flex items-end justify-start relative">
          <iframe
            width="100%"
            height="100%"
            title="map"
            className="absolute inset-0"
            frameBorder={0}
            marginHeight={0}
            marginWidth={0}
            style={{ filter: "opacity(0.7)" }}
            src="https://www.google.com/maps/embed/v1/place?q=97+warren+st+new+york+city&key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8"
          />
          <div className="bg-gray-900 relative flex flex-wrap py-6 rounded shadow-md">
            <div className="lg:w-1/2 px-6">
              <h2 className="title-font font-semibold text-white tracking-widest text-xs">
                ADDRESS
              </h2>
              <p className="mt-1">
                97 Warren St. <br />
                New York, NY 10007
              </p>
            </div>
            <div className="lg:w-1/2 px-6 mt-4 lg:mt-0">
              <h2 className="title-font font-semibold text-white tracking-widest text-xs">
                EMAIL
              </h2>
              <a className="text-indigo-400 leading-relaxed">
                reedbarger@email.com
              </a>
              <h2 className="title-font font-semibold text-white tracking-widest text-xs mt-4">
                PHONE
              </h2>
              <p className="leading-relaxed">123-456-7890</p>
            </div>
          </div>
        </div>
        <form
          netlify
          name="contact"
          onSubmit={handleSubmit}
          className="lg:w-1/3 md:w-1/2 flex flex-col md:ml-auto w-full md:py-8 mt-8 md:mt-0">
          <h2 className="text-white sm:text-4xl text-3xl mb-1 font-medium title-font">
            Hire Me
          </h2>
          <p className="leading-relaxed mb-5">
            Lorem ipsum dolor sit amet consectetur, adipisicing elit. Illum
            suscipit officia aspernatur veritatis. Asperiores, aliquid?
          </p>
          <div className="relative mb-4">
            <label htmlFor="name" className="leading-7 text-sm text-gray-400">
              Name
            </label>
            <input
              type="text"
              id="name"
              name="name"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              onChange={(e) => setName(e.target.value)}
            />
          </div>
          <div className="relative mb-4">
            <label htmlFor="email" className="leading-7 text-sm text-gray-400">
              Email
            </label>
            <input
              type="email"
              id="email"
              name="email"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 text-base outline-none text-gray-100 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              onChange={(e) => setEmail(e.target.value)}
            />
          </div>
          <div className="relative mb-4">
            <label
              htmlFor="message"
              className="leading-7 text-sm text-gray-400">
              Message
            </label>
            <textarea
              id="message"
              name="message"
              className="w-full bg-gray-800 rounded border border-gray-700 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-900 h-32 text-base outline-none text-gray-100 py-1 px-3 resize-none leading-6 transition-colors duration-200 ease-in-out"
              onChange={(e) => setMessage(e.target.value)}
            />
          </div>
          <button
            type="submit"
            className="text-white bg-indigo-500 border-0 py-2 px-6 focus:outline-none hover:bg-indigo-600 rounded text-lg">
            Submit
          </button>
        </form>
      </div>
    </section>
  );
}

Como você pode ver acima, estamos inserindo no código os dados do formulário com uma função especial, chamada encode que você vê acima.

Se a mensagem for enviada corretamente, exibiremos um alerta que diz "Message sent" (em português, "mensagem enviada"). Caso contrário, se houver um erro, vamos alertar o usuário sobre esse erro.

Como criar o componente Navbar

O último passo é desenvolver nosso componente Navbar.

Queremos que essa barra de navegação fique na parte superior da nossa aplicação em dispositivos grandes e que não fique presa acima em dispositivos móveis.

Além disso, queremos incluir links para cada uma de nossas seções relevantes: a seção de depoimentos, a de habilidades, a de projetos e a do nosso formulário de contato:

// src/components/Navbar.js

import { ArrowRightIcon } from "@heroicons/react/solid";
import React from "react";

export default function Navbar() {
  return (
    <header className="bg-gray-800 md:sticky top-0 z-10">
      <div className="container mx-auto flex flex-wrap p-5 flex-col md:flex-row items-center">
        <a className="title-font font-medium text-white mb-4 md:mb-0">
          <a href="#about" className="ml-3 text-xl">
            Reed Barger
          </a>
        </a>
        <nav className="md:mr-auto md:ml-4 md:py-1 md:pl-4 md:border-l md:border-gray-700	flex flex-wrap items-center text-base justify-center">
          <a href="#projects" className="mr-5 hover:text-white">
            Past Work
          </a>
          <a href="#skills" className="mr-5 hover:text-white">
            Skills
          </a>
          <a href="#testimonials" className="mr-5 hover:text-white">
            Testimonials
          </a>
        </nav>
        <a
          href="#contact"
          className="inline-flex items-center bg-gray-800 border-0 py-1 px-3 focus:outline-none hover:bg-gray-700 rounded text-base mt-4 md:mt-0">
          Hire Me
          <ArrowRightIcon className="w-4 h-4 ml-1" />
        </a>
      </div>
    </header>
  );
}

Como ela fica na parte superior da página em um dispositivo maior? Com a ajuda da classe md:sticky em nosso elemento header.

Essa classe significa que ela terá aplicada a ela a regra de estilo position: sticky; a partir de um tamanho médio (768px).

Como fazer a implantação do seu portfólio

Agora, para tornar nosso portfólio público, precisamos enviar nossa aplicação para o GitHub.

Se você não está familiarizado com o Git e o GitHub, eu dedicaria um tempo para aprender a enviar seu código para sua conta do GitHub pela primeira vez. É uma habilidade de conhecimento essencial para qualquer desenvolvedor.

Quando você estiver familiarizado com esse processo, podemos, primeiro, criar um novo repositório Github. Depois disso, executaremos git add ., git commit -m "Deploy", criaremos nosso git remote e usaremos git push -u origin main.

Com o nosso projeto enviado para o GitHub, podemos acessar o Netlify e selecionar a opção "Choose Site from Git" (em português, "escolher site do Git"). Em seguida, escolheremos o GitHub para nossa implantação contínua e escolheremos o repositório do GitHub para o qual acabamos de enviar nosso código.

portfolio-3-min

Depois disso, nosso projeto estará automaticamente visível na web!

O que fazer depois disso?

Parabéns! Agora você tem uma aplicação de portfólio pública na web que mostra todos os seus projetos e habilidades para potenciais empregadores.

O próximo passo a ser tomado será configurar um domínio personalizado, de preferência com seu nome (como, reedbarger.com, por exemplo). Como o Netlify inclui um DNS, você pode facilmente configurar um domínio personalizado com eles.

Procure talvez adicionar um blog à sua aplicação do React para mostrar ainda mais o seu conhecimento de desenvolvedor para potenciais empregadores.

Faça do seu portfólio pessoal uma expressão de si mesmo e das coisas que você, como desenvolvedor, é apaixonado e certamente terá sucesso!

Torne-se um desenvolvedor profissional em React

React é difícil. Você não precisa ter que descobrir isso sozinho.

Coloquei tudo o que sei sobre o React em um único curso, para ajudá-lo a alcançar seus objetivos em tempo recorde. É o curso que eu gostaria de ter feito quando comecei a aprender React.

react-bootcamp-cta-alt

Clique aqui para acessar o curso (em inglês).