Skip to main content

Overview

The JobCard component displays a single job listing with all relevant information and actions. It includes an “Apply” button with state management and a link to view detailed job information.

Usage

import { JobCard } from '@/components'

function JobsList() {
  const job = {
    id: "1",
    titulo: "Senior React Developer",
    empresa: "Tech Corp",
    ubicacion: "Remoto",
    descripcion: "We are looking for an experienced React developer...",
    data: {
      modalidad: "remoto",
      nivel: "senior",
      technology: "react"
    }
  }

  return <JobCard job={job} />
}

Props

job
object
required
The job object containing all job information

Features

Apply Button State Management

The component manages its own “applied” state using React’s useState hook:
const [isApplied, setIsApplied] = useState(false)

const handleApplyClick = () => {
  setIsApplied(true)
}
When clicked, the button changes from “Aplicar” to “Aplicado” and applies a different CSS class.

Data Attributes

The card uses data attributes for filtering and styling purposes:
<article
  className="job-listing-card"
  data-modalidad={job.data.modalidad}
  data-nivel={job.data.nivel}
  data-technology={job.data.technology}
>
These attributes can be used for CSS selectors or JavaScript filtering. Includes two navigation options:
  1. Clickable job title that links to the full job details
  2. “Ver detalles” (View details) link button
Both navigate to /jobs/{job.id}.

Component Structure

import { useState } from "react"
import { Link } from "@/components"
import styles from "./jobCard.module.css"

export function JobCard({ job }) {
  const [isApplied, setIsApplied] = useState(false)

  const handleApplyClick = () => {
    setIsApplied(true)
  }

  const buttonClasses = isApplied ? 'button-apply-job is-applied' : 'button-apply-job'
  const buttonText = isApplied ? 'Aplicado' : 'Aplicar'

  return (
    <article
      className="job-listing-card"
      data-modalidad={job.data.modalidad}
      data-nivel={job.data.nivel}
      data-technology={job.data.technology}
    >
      <div className={styles.info}>
        <h3>
          <Link className={styles.title} href={`/jobs/${job.id}`}>
            {job.titulo}
          </Link>
        </h3>
        <small>{job.empresa} | {job.ubicacion}</small>
        <p>{job.descripcion}</p>
      </div>

      <div className={styles.actions}>
        <Link href={`/jobs/${job.id}`} className={styles.details}>
          Ver detalles
        </Link>
        <button className={buttonClasses} onClick={handleApplyClick}>
          {buttonText}
        </button>
      </div>
    </article>
  )
}

Styling

The component uses CSS modules (jobCard.module.css) for scoped styling:
  • styles.info - Container for job information
  • styles.title - Job title link styling
  • styles.actions - Container for action buttons
  • styles.details - “Ver detalles” link styling
Global classes are used for:
  • job-listing-card - Main card container
  • button-apply-job - Apply button base styling
  • is-applied - Applied state modifier

State Management

Local State

The component maintains its own isApplied state, which persists only during the component’s lifetime. The state is reset if:
  • The component unmounts
  • The page is refreshed
  • The user navigates away and back

Persistent State

For production applications, you might want to persist the applied state:
import { useState, useEffect } from "react"

function JobCard({ job }) {
  const [isApplied, setIsApplied] = useState(() => {
    // Check localStorage for applied jobs
    const applied = localStorage.getItem(`job-applied-${job.id}`)
    return applied === 'true'
  })

  const handleApplyClick = () => {
    setIsApplied(true)
    localStorage.setItem(`job-applied-${job.id}`, 'true')
  }
  
  // ...
}

Source Code

Location: src/components/JobCard/JobCard.jsx:5

JobListings

Container component that renders multiple JobCards

Loading

Skeleton loading state for JobCards

Best Practices

  1. Always provide a key - When rendering multiple JobCards, use the job.id as the React key
  2. Complete job data - Ensure all required job properties are present to avoid errors
  3. Consistent structure - Maintain the expected job object structure across your application
  4. Handle missing data - Consider adding fallbacks for optional fields

Accessibility

  • Uses semantic <article> element for each job listing
  • Proper heading hierarchy with <h3> for job titles
  • Descriptive link text (“Ver detalles”)
  • Button state changes are visually indicated
  • Data attributes support custom filtering interfaces

Build docs developers (and LLMs) love