3 min read

Build the Linkedin "Add experience" form

Build a dynamic form inspired from Linkedin's "Add experience" form.
This is an advanced challenge where you get to practice building dynamic forms and using Typescript.

0:00
/0:09

The form should have the following fields:

  • job title (required)
  • employment type
  • company (required)
  • whether the job is current or not (checkbox)
  • start date (required)
  • end date (disabled if job is current, required otherwise)

Use Typescript for your solution. Use react-hook-form to build the form and zod for validation.

The form should show the same error messages the Linkedin form shows.

You can use this sample data to get started:

// sample-data.ts
import { JobExperience } from "./types";

export const sampleJobExperiences: JobExperience[] = [
  {
    job_title: "Software Engineer",
    employment_type: "Full-time",
    company: "TechCorp Inc.",
    is_current: true,
    start_date: { month: "June", year: "2020" },
  },
  {
    job_title: "Frontend Developer",
    employment_type: "Contract",
    company: "Creative Solutions Ltd.",
    is_current: false,
    start_date: { month: "January", year: "2018" },
    end_date: { month: "May", year: "2020" },
  },
];

Also, since the modal and showing the list of job experiences is not the main focus of this challenge (building the form is 😄), you can copy the code for them if you want. You will be left with implementing the JobExperience type and the AddExperienceForm component.

// App.tsx
import { useState } from "react";
import { JobExperience } from "./types";
import AddExperienceForm from "./components/AddExperienceForm";
import { sampleJobExperiences } from "./sample-data";

function App() {
  const [experienceList, setExperienceList] =
    useState<JobExperience[]>(sampleJobExperiences);
  const [showAddExperienceForm, setShowAddExperienceForm] = useState(false);
  return (
    <div className="bg-gray-300 min-h-screen p-4">
      <div className="container mx-auto p-4 max-w-4xl border-1 border-gray-400 rounded-xl bg-white shadow-lg">
        <h1 className="text-3xl font-bold mb-6">My Linkedin Profile</h1>
        <div className="flex items-center justify-between p-4 bg-gray-200">
          <h1 className="text-2xl font-bold">Experience</h1>
          <button
            onClick={() => setShowAddExperienceForm(true)}
            className="cursor-pointer hover:bg-gray-100 p-2 rounded-2xl"
          >
            Add
          </button>
        </div>
        {experienceList.length === 0 && (
          <div className="flex items-center justify-center h-64">
            <p className="text-gray-500">No experience added yet.</p>
          </div>
        )}
        {experienceList.map((jobExperience) => (
          <div className="p-4" key={jobExperience.job_title}>
            <h2 className="text-xl font-semibold">{jobExperience.job_title}</h2>
            <p>{jobExperience.company}</p>
            <p className="text-gray-500">
              {jobExperience.start_date.month} {jobExperience.start_date.year}
              {jobExperience.is_current
                ? " - Present"
                : ` - ${jobExperience.end_date?.month} ${jobExperience.end_date?.year}`}
            </p>
          </div>
        ))}
        {showAddExperienceForm && (
          <dialog className="fixed inset-0 bg-black/60 flex items-baseline pt-10 justify-center w-full h-full">
            <div className="bg-white rounded shadow-lg container w-3xl">
              <AddExperienceForm
                onSubmit={(newExperience) => {
                  setExperienceList((prev) => [newExperience, ...prev]);
                  setShowAddExperienceForm(false);
                }}
                onCancel={() => {
                  setShowAddExperienceForm(false);
                }}
              />
            </div>
          </dialog>
        )}
      </div>
    </div>
  );
}

export default App;
💡
Ready to check your work?
Checkout the official solution and the comments section for feedback and discussion.

Get the React Practice Calendar!

28 days of focused practice of increasing difficulty, going through everything from Fundamentals, Data fetching, Forms and using Intervals in React.

You will also get notified whenever a new challenge is published.