CV Creator

Motivation

I needed to update my CV, so naturally, I did what any reasonable developer would do: I spent countless hours creating an app to make CVs instead. Why update one CV when you can build a tool to create infinite CVs, right?


Project Overview

CV Creator is a sophisticated web application designed to streamline the resume creation process aimed at students like myself. By leveraging cutting-edge web technologies, this project transforms the often tedious task of resume writing into an intuitive, dynamic experience. The application combines the ease of use typically associated with modern social media platforms with the power to craft professional, customizable documents.

Core Technologies

  • React: For building a dynamic and responsive user interface
  • Vite: Bundler & Build tool
  • Tailwind CSS: For efficient, utility-first styling
  • Framer Motion: For smooth animations and drag-and-drop functionality
  • @react-pdf/renderer: For generating high-quality PDF documents
  • @react-hook/resize-observer: For responsive design implementation

Key Features and Technical Implementation

Real-Time Preview with Dynamic Scaling

The CV Creator offers a real-time preview that updates instantaneously with user input. This feature is implemented using React’s state management and efficient re-rendering capabilities.

Dynamic Scaling Implementation
const [scale, setScale] = useState(1);
const previewContainerRef = useRef(null);

useResizeObserver(previewContainerRef, (entry) => {
const { width, height } = entry.contentRect;
const docWidth = DOC_WIDTH_MM * MM_TO_PIXEL;
const docHeight = DOC_HEIGHT_MM * MM_TO_PIXEL;

const scaleByWidth = width / docWidth;
const scaleByHeight = height / docHeight;
const newScale = Math.min(scaleByWidth, scaleByHeight) - 0.1;

setScale(Math.max(0.2, Math.min(newScale, 0.8)));
});

return (
<div ref={previewContainerRef} style={{ transform: `scale(${scale})` }}>
  <PDFRenderer
    resumeData={resumeData}
    selectedSocial={socialButton}
    sectionsOrder={sectionsOrder}
  />
</div>
);

This implementation ensures that the preview scales appropriately across various screen sizes while maintaining the aspect ratio of an A4 document.

Custom PDF Rendering Engine

Instead of relying on traditional PDF viewers or converters, CV Creator utilizes a custom PDF rendering engine built with React and Tailwind CSS. This approach offers several advantages:

  • Text selectability
  • Improved performance
  • Consistent styling between preview and final PDF

The rendering engine is implemented as a React component:

Custom PDF Renderer Component
const PDFRenderer = ({ resumeData, selectedSocial, sectionsOrder }) => {
return (
  <div className="m-10 flex h-[297mm] w-[210mm] flex-col items-center">
    <span className="text-[40px]">{resumeData.fullName}</span>
    {/* Render contact information */}
    {sectionsOrder.map((section, index) => {
      if (resumeData.includeSections[section]) {
        return (
          <div className="w-full text-[20px]" key={index}>
            {renderSection(section)}
          </div>
        );
      }
      return null;
    })}
  </div>
);
};

This component structure allows for easy customization and ensures that the preview accurately reflects the final PDF output.

Section Reordering with Drag Functionality

CV Creator implements a flexible section ordering system using Framer Motion’s Reorder component:

Section Reordering
import { Reorder } from 'framer-motion';

<Reorder.Group values={sectionsOrder} onReorder={sectionOrderHandler}>
{sectionsOrder.map((section) => (
  <DraggableItem
    key={section}
    item={section}
    renderSection={renderSection}
    isReorderingEnabled={uiState.reorderToggle}
  />
))}
</Reorder.Group>

The DraggableItem component encapsulates the drag-and-drop functionality for each section:

Draggable Item Component
const DraggableItem = ({ item, renderSection, isReorderingEnabled }) => {
const dragControls = useDragControls();

return (
  <Reorder.Item
    value={item}
    id={item}
    dragListener={false}
    dragControls={dragControls}
  >
    {/* Drag handle and section content */}
  </Reorder.Item>
);
};

This implementation allows users to easily customize their resume structure, enhancing the flexibility of the application.

Custom Sections and Data Management

CV Creator supports user-defined custom sections, demonstrating the application’s adaptability to various resume formats:

Custom Section Handler
function handleCustomSection(updatedCustomSectionData) {
setResumeData((prevData) => ({
  ...prevData,
  customSectionData: updatedCustomSectionData,
}));
}

Custom sections are managed through a dedicated component that allows for adding, editing, and reordering items within each section:

Custom Section Card Component
function CustomSectionCard({ sectionTitle, sectionItemsList, onSectionUpdate }) {
// Component logic for managing custom section items
}

This feature showcases the application’s ability to handle dynamic data structures and provide a highly personalized user experience.

State Persistence with Local Storage

To ensure a seamless user experience across sessions, CV Creator implements state persistence using local storage:

Local Storage State Persistence
useEffect(() => {
localStorage.setItem('resumeData', JSON.stringify(resumeData));
}, [resumeData]);

const [resumeData, setResumeData] = useState(() => {
const savedData = localStorage.getItem('resumeData');
return savedData ? JSON.parse(savedData) : defaultResumeData;
});

This implementation ensures that users can seamlessly continue their work across multiple sessions without data loss.

Technical Challenges and Solutions

Throughout the development of CV Creator, several technical challenges were encountered and addressed:

Performance Optimization:

  • Challenge: Rendering large resumes in real-time initially caused performance issues.
  • Solution: Implemented debouncing techniques and optimized the rendering process to ensure smooth updates, particularly in the real-time preview component.

Responsive Design:

  • Challenge: Achieving a consistent layout across various devices and screen sizes.
  • Solution: Utilized a combination of Tailwind CSS for responsive styling and custom scaling logic with useResizeObserver to maintain proper dimensions and aspect ratios.

State Management Complexity:

  • Challenge: As the application grew, managing state became increasingly complex.
  • Solution: Refactored the state management approach, implementing a more modular structure with clearly defined update functions for each data type (e.g., handleEducationUpdate, handleExperienceUpdate).

PDF Generation Accuracy:

  • Challenge: Ensuring the generated PDF exactly matched the preview.
  • Solution: Developed a custom rendering engine that uses the same components for both preview and PDF generation, ensuring consistency.

Want to Contribute?

CV Creator provides numerous opportunities for further development and is open to contribution:

  • Template Expansion: Develop additional resume templates to cater to various industries and personal styles.
  • Accessibility Enhancements: Improve the application’s accessibility features to ensure it’s usable by individuals with diverse needs.
  • Performance Optimization: Further optimize the application for handling very large resumes or complex layouts.
  • AI Integration: Implement machine learning algorithms to provide intelligent content suggestions for resume sections.

For developers and students alike looking to contribute or learn from this project, CV Creator offers a rich playground for exploring React hooks, custom rendering techniques, state management patterns, and the integration of various modern web libraries and frameworks.


To explore the code, contribute to the project, or use CV Creator for your own resume, visit the GitHub repository. I welcome fellow students and peers alike, to try and contribute!