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.
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;
Checkout the official solution and the comments section for feedback and discussion.
Member discussion