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.
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:
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:
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:
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:
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:
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:
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!