In this post I am going to show a React component to render JSON Resume.
As usual enough words, see the code 😄
import React from 'react'; import { Github, Linkedin } from '@icons-pack/react-simple-icons'; import { DateTime } from 'luxon'; const Resume = ({ resume, style }) => { return ( <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', ...style, }} > <div style={{ display: 'flex' }}> <div style={{ width: 300, marginRight: 80 }}> <div style={{ display: 'flex', justifyContent: 'center' }}> <img src={resume.basics.picture} style={{ height: 150, borderRadius: '100%' }} /> </div> <div> <h3 style={{ marginBottom: '0.5em' }}>SUMMARY</h3> <p>{resume.basics.summary}</p> </div> <div style={{ marginBottom: '0.5em' }}> <h3 style={{ marginBottom: '0.5em' }}>CONTACT & PROFILES</h3> <div> <a href={`mailto:${resume.basics.email}`}> {resume.basics.email} </a> </div> <div>{resume.basics.phone}</div> <div> <a href={resume.basics.website} target="_blank"> {resume.basics.website} </a> </div> {(resume.basics.profiles || []).map((p, k) => ( <div key={k}> <a href={p.url} target="_blank" style={{ display: 'inline-flex', alignItems: 'center' }} > <Icon url={p.url} /> {p.username} </a> </div> ))} </div> {isEmpty(resume.languages) ? null : ( <div style={{ marginBottom: '0.5em' }}> <h3 style={{ marginBottom: '0.5em' }}>LANGUAGES</h3> {(resume.languages || []).map((item, k) => ( <div key={k}> <span>{item.language}</span> <Tag>{item.fluency}</Tag> </div> ))} </div> )} {isEmpty(resume.interests) ? null : ( <div> <h3 style={{ marginBottom: '0.5em' }}>INTERESTS & HOBBIES</h3> {(resume.interests || []).map((item, k) => ( <div key={k}> <span>{item.name}</span> <div> {(item.keywords || []).map((tag, k) => ( <Tag key={k} color="secondary"> {tag} </Tag> ))} </div> </div> ))} </div> )} </div> <div> <div style={{ marginTop: 40 }}> <h1>{resume.basics.name}</h1> <h5 style={{ textTransform: 'uppercase', marginTop: -20 }}> {resume.basics.label} </h5> </div> <div style={{ marginTop: 70, marginBottom: '1em' }}> <h3 style={{ marginBottom: '0.5em' }}>SKILLS</h3> {(resume.skills || []).map((item, k) => ( <div key={k}> <div> <strong>{item.name}</strong> <Tag>{item.level}</Tag> </div> <div> {(item.keywords || []).map((tag, k) => ( <Tag key={k} color="secondary"> {tag} </Tag> ))} </div> </div> ))} </div> <Education education={resume.education || []} /> <Experience items={resume.work || []} /> <Experience items={resume.volunteer || []} /> </div> </div> </div> ); }; const Icon = ({ url }) => { const size = 20; const style = { marginRight: 4 }; const u = url.toLowerCase(); if (u.indexOf('github') >= 0) { return <Github size={size} color="black" style={style} />; } if (u.indexOf('linkedin') >= 0) { return <Linkedin size={size} color="black" style={style} />; } return null; }; const Experience = ({ items }) => isEmpty(items) ? null : ( <div style={{ marginTop: '0.5em' }}> <h3 style={{ marginBottom: '0.5em' }}>WORK EXPERIENCE</h3> {items.map((item, k) => ( <div key={k} style={{ position: 'relative' }}> {items.length === 1 ? null : ( <Timeline isLast={k === items.length - 1} /> )} <h3 style={{ marginBottom: 0 }}>{item.position}</h3> <h5 style={{ margin: '2px 0', display: 'flex', justifyContent: 'space-between', }} > <a href={item.website} target="_blank"> {item.company || item.organization} </a> <Period startDate={item.startDate} endDate={item.endDate} /> </h5> <p>{item.summary}</p> </div> ))} </div> ); const Education = ({ education }) => isEmpty(education) ? null : ( <div style={{ marginTop: '0.5em' }}> <h3 style={{ marginBottom: '0.5em' }}>EDUCATION</h3> {education.map((item, k) => ( <div key={k} style={{ position: 'relative' }}> {education.length === 1 ? null : ( <Timeline isLast={k === education.length - 1} /> )} <h3 style={{ marginBottom: 0 }}>{item.institution}</h3> <h5 style={{ margin: '2px 0', display: 'flex', justifyContent: 'space-between', }} > <a href={item.website} target="_blank"> {item.website} </a> <Period startDate={item.startDate} endDate={item.endDate} /> </h5> <p>{item.area}</p> </div> ))} </div> ); const Timeline = ({ isLast }) => { return ( <> <div style={{ position: 'absolute', width: 24, height: 24, border: '1px solid var(--textLink)', borderRadius: '100%', left: -30, top: 3, padding: 6, }} > <div style={{ width: 10, height: 10, backgroundColor: 'var(--textLink)', borderRadius: '100%', }} ></div> </div> {isLast ? null : ( <div style={{ position: 'absolute', width: 2, left: -19, top: 26, bottom: -31, backgroundColor: 'var(--textLink)', }} ></div> )} </> ); }; const Period = ({ startDate, endDate }) => ( <span style={{ width: 130 }}> <span> {DateTime.fromFormat(startDate, 'yyyy-MM-dd').toFormat('MMM yyyy')} </span> <span> - </span> <span> {endDate ? DateTime.fromFormat(endDate, 'yyyy-MM-dd').toFormat('MMM yyyy') : 'now'} </span> </span> ); const Tag = ({ color, children }) => { return ( <span style={{ backgroundColor: color === 'secondary' ? '#48BFE3' : 'var(--textLink)', color: 'white', borderRadius: 4, padding: '0px 4px 2px 4px', marginLeft: 4, boxSizing: 'border-box', fontSize: 'smaller', fontWeight: 'bold', }} > {children} </span> ); }; function isEmpty(a) { return !a || a.length === 0; }
The final result can be seen here.
Enjoy! EOF 😄
Top comments (1)
Just made fixes for dark mode. Please check it out at tsvbits.com/cv/