Skip to main content

Overview

Form components provide the user interface for editing different sections of a CV. Each form component handles its own section of the CVData structure using React Hook Form.

Source Files

  • PersonalDetailsForm: components/forms/PersonalDetailsForm.tsx
  • ExperienceForm: components/forms/ExperienceForm.tsx
  • EducationForm: components/forms/EducationForm.tsx
  • ProjectsForm: components/forms/ProjectsForm.tsx
  • SkillsForm: components/forms/SkillsForm.tsx
  • AchievementsForm: components/forms/AchievementsForm.tsx
  • LanguagesForm: components/forms/LanguagesForm.tsx
  • ReferencesForm: components/forms/ReferencesForm.tsx

Common Patterns

All form components follow consistent patterns:
  1. Form Context: Access form methods via useFormContext<CVData>()
  2. Field Arrays: List-based sections use useFieldArray for dynamic items
  3. Controllers: Form fields wrapped in Controller for controlled components
  4. Section Visibility: Check hiddenSections to determine if section is hidden
  5. Drag & Drop: Reorderable items use Framer Motion’s Reorder components

PersonalDetailsForm

Edits personal information and professional summary.

Usage

import { PersonalDetailsForm } from "@/components/forms/PersonalDetailsForm";

<PersonalDetailsForm template="rhyhorn" />;

Props

template
TemplateId
required
Current template ID to determine feature availability (e.g., profile image support)

Fields

The form manages the following fields under personalInfo:
fullName
string
required
User’s full name
jobTitle
string
required
Professional job title or desired position
email
string
Email address
phone
string
Phone number
address
string
Location or full address
linkedin
string
LinkedIn profile URL
github
string
GitHub profile URL
website
string
Personal website or portfolio URL
summary
string
Professional summary or career objective (multi-line text)
profileImageUrl
string
Profile image URL (only shown for templates that support profile images)

Profile Image Support

Profile image upload is conditionally rendered based on template capabilities:
const supportsProfileImage =
  profileImageEnabledTemplates.includes(template) &&
  getTemplateById(template).supportsProfileImage;
Currently supported templates: rhyhorn, nexus

Example

<Controller
  control={control}
  name="personalInfo.fullName"
  render={({ field: { value, onChange } }) => (
    <Input
      value={value || ""}
      onChange={onChange}
      placeholder="TARIQ AHMAD"
    />
  )}
/>

ExperienceForm

Manages work experience entries with collapsible items.

Usage

import { ExperienceForm } from "@/components/forms/ExperienceForm";

<ExperienceForm />;

Props

No props required. Uses form context.

Fields

Each experience item contains:
id
string
Unique identifier (auto-generated)
company
string
Company or organization name
role
string
Job title or role
startDate
string
Start date (format: MM/YYYY)
endDate
string
End date (format: MM/YYYY or “Present”)
current
boolean
Whether this is the current position
description
string
Job responsibilities and achievements (multi-line, bullet points supported)

Features

  • Drag & Drop: Reorder items using drag handle
  • Collapsible: Items can be expanded/collapsed
  • Auto-expand: New items automatically expand
  • Empty State: Shows helpful message when no items exist

Reordering

const { fields, append, remove, move } = useFieldArray({
  control,
  name: "experience",
  keyName: "_id",
});

const handleReorder = (newOrder: typeof fields) =>
  handleFieldArrayReorder(fields, newOrder, move);

EducationForm

Manages education and certification entries.

Usage

import { EducationForm } from "@/components/forms/EducationForm";

<EducationForm />;

Fields

Each education item contains:
id
string
Unique identifier (auto-generated)
school
string
School or university name
degree
string
Degree, diploma, or certification name
startDate
string
Start date (format: MM/YYYY)
endDate
string
End date or expected graduation (format: MM/YYYY)
description
string
Additional details (optional)

Example

append({
  id: generateId(),
  school: "",
  degree: "",
  startDate: "",
  endDate: "",
  description: "",
});

ProjectsForm

Showcase personal or professional projects.

Usage

import { ProjectsForm } from "@/components/forms/ProjectsForm";

<ProjectsForm />;

Fields

Each project item contains:
id
string
Unique identifier (auto-generated)
title
string
Project name or title
date
string
Project date or time period
techStack
string
Technologies used (displayed as “Skill: …” in CV)
description
string
Project description and outcomes

SkillsForm

Manage technical and professional skills.

Usage

import { SkillsForm } from "@/components/forms/SkillsForm";

<SkillsForm />;

Fields

Each skill item contains:
id
string
Unique identifier (auto-generated)
name
string
Skill name or category (e.g., “Programming Languages”)
description
string
Skill values or details (e.g., “JavaScript, Python, Java”)
category
'technical' | 'professional'
Skill category:
  • technical: Technical skills
  • professional: Professional attributes

Category Selection

<select>
  <option value="technical">Technical Skill</option>
  <option value="professional">Professional Attribute</option>
</select>
Skills are grouped by category in the CV preview.

AchievementsForm

Track awards, certifications, and recognition.

Usage

import { AchievementsForm } from "@/components/forms/AchievementsForm";

<AchievementsForm />;

Fields

Each achievement item contains:
id
string
Unique identifier (auto-generated)
title
string
Achievement or award title
organization
string
Issuing organization or institution
date
string
Date received or achieved

LanguagesForm

List languages and proficiency levels.

Usage

import { LanguagesForm } from "@/components/forms/LanguagesForm";

<LanguagesForm />;

Fields

Each language item contains:
id
string
Unique identifier (auto-generated)
name
string
Language name
proficiency
string
Proficiency level (e.g., “Native”, “Fluent”, “Conversational”)

ReferencesForm

Simple text field for references section.

Usage

import { ReferencesForm } from "@/components/forms/ReferencesForm";

<ReferencesForm />;

Fields

references
string
References text (typically “Available upon request” or contact details)

Shared Components

FormSectionCard

All form components are wrapped in FormSectionCard for consistent styling:
<FormSectionCard
  title="Work Experience"
  subtitle="Your professional journey"
  icon={Briefcase}
  accentClassName="bg-gradient-to-r from-blue-500 to-cyan-500"
  iconWrapperClassName="bg-gradient-to-br from-blue-500/10 to-cyan-500/10"
  iconClassName="text-blue-600 dark:text-blue-400"
  contentClassName="space-y-4"
  isHidden={isHidden}
>
  {/* Form content */}
</FormSectionCard>

Collapsible Items

List-based forms use a common collapsible item pattern:
function ExperienceItem({ field, index, isExpanded, onToggle }) {
  return (
    <Reorder.Item value={field}>
      {/* Header - Always visible */}
      <div onClick={onToggle}>{/* Title */}</div>

      {/* Expandable Content */}
      <AnimatePresence>
        {isExpanded && <motion.div>{/* Form fields */}</motion.div>}
      </AnimatePresence>
    </Reorder.Item>
  );
}

Validation

Forms use React Hook Form’s built-in validation:
<Controller
  control={control}
  name="personalInfo.email"
  rules={{
    pattern: {
      value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
      message: "Invalid email address",
    },
  }}
  render={({ field, fieldState: { error } }) => (
    <>
      <Input {...field} />
      {error && <span>{error.message}</span>}
    </>
  )}
/>

Accessibility

  • Labels: All form fields have associated labels
  • Required Fields: Marked with asterisk and aria-required
  • Error Messages: Announced to screen readers
  • Keyboard Navigation: Full keyboard support for all interactions
  • Focus Management: Focus moves to fields when sections expand

Build docs developers (and LLMs) love