Skip to main content

Overview

The SearchFormSection component provides a comprehensive search interface for job listings. It includes a text search input and multiple filter dropdowns (technology, location, experience level) with real-time updates.

Usage

import { SearchFormSection } from '@/components'
import { useState } from 'react'

function JobSearchPage() {
  const [searchText, setSearchText] = useState('')
  const [filters, setFilters] = useState({})
  
  const handleTextFilter = (text) => {
    setSearchText(text)
  }
  
  const handleSearch = (formData) => {
    setFilters(formData)
    // Apply filters to job listings
  }
  
  return (
    <SearchFormSection
      onTextFilter={handleTextFilter}
      onSearch={handleSearch}
      initialTextInput={searchText}
    />
  )
}

Props

onTextFilter
function
required
Callback function called when the search text changes. Receives the current text value.
onTextFilter: (text: string) => void
Callback function called when any form field changes. Receives the complete form data object.
onSearch: (formData: FormData) => void
initialTextInput
string
Initial value for the text search input field

Features

The main search input allows users to search for jobs, companies, or skills:
<input
  name={idText}
  id="empleos-search-input"
  type="text"
  ref={inputRef}
  defaultValue={initialTextInput}
  placeholder="Buscar trabajos, empresas o habilidades"
  onChange={handleTextChange}
/>

Technology Filter

Dropdown with popular technologies organized in groups:
  • Popular Technologies: JavaScript, Python, React, Node.js
  • Other Technologies: Java, C#, C, C++, Ruby, PHP
<select name={idTechnology} id="filter-technology" ref={selectRefTechnology}>
  <option value="">Tecnología</option>
  <optgroup label="Tecnologías populares">
    <option value="javascript">JavaScript</option>
    <option value="python">Python</option>
    <option value="react">React</option>
    <option value="nodejs">Node.js</option>
  </optgroup>
  {/* More options... */}
</select>

Location Filter

Dropdown with available job locations:
  • Remoto
  • Ciudad de México
  • Guadalajara
  • Monterrey
  • Barcelona

Experience Level Filter

Dropdown with experience levels:
  • Junior
  • Mid-level
  • Senior
  • Lead

Clear Filters Button

A clear button (“x”) that resets all form fields:
<button onClick={handleClearFilters} type="button" aria-label="Clear search input" className="clear-input-button">
  x
</button>

Component Structure

import { useId } from "react"
import { useSearchForm } from "@/hooks/useSearchForm"

export function SearchFormSection({ onTextFilter, onSearch, initialTextInput }) {
  const idText = useId()
  const idTechnology = useId()
  const idLocation = useId()
  const idExperienceLevel = useId()

  const { 
    handleSubmit, 
    handleTextChange, 
    handleClearFilters, 
    inputRef, 
    selectRefTechnology, 
    selectRefLocation, 
    selectRefExperienceLevel 
  } = useSearchForm({ 
    onTextFilter, 
    onSearch, 
    idTechnology, 
    idLocation, 
    idExperienceLevel, 
    idText 
  })

  return (
    <section className="jobs-search">
      <h1>Encuentra tu próximo trabajo</h1>
      <p>Explora miles de oportunidades en el sector tecnológico.</p>

      <form onChange={handleSubmit} id="empleos-search-form" role="search">
        <div className="search-bar">
          <svg>{/* Search icon */}</svg>
          <input
            name={idText}
            type="text"
            ref={inputRef}
            defaultValue={initialTextInput}
            placeholder="Buscar trabajos, empresas o habilidades"
            onChange={handleTextChange}
          />
          <button onClick={handleClearFilters} type="button" aria-label="Clear search input">
            x
          </button>
        </div>

        <div className="search-filters">
          <select name={idTechnology} ref={selectRefTechnology}>
            {/* Technology options */}
          </select>
          <select name={idLocation} ref={selectRefLocation}>
            {/* Location options */}
          </select>
          <select name={idExperienceLevel} ref={selectRefExperienceLevel}>
            {/* Experience level options */}
          </select>
        </div>
      </form>
    </section>
  )
}

Custom Hook Integration

The component uses the useSearchForm custom hook to manage form state and behavior:
const { 
  handleSubmit,        // Form submit handler
  handleTextChange,    // Text input change handler  
  handleClearFilters,  // Clear all filters
  inputRef,            // Ref for text input
  selectRefTechnology, // Ref for technology select
  selectRefLocation,   // Ref for location select
  selectRefExperienceLevel // Ref for experience select
} = useSearchForm({ 
  onTextFilter, 
  onSearch, 
  idTechnology, 
  idLocation, 
  idExperienceLevel, 
  idText 
})

Form Data Structure

The onSearch callback receives a FormData object with the following structure:
{
  [idText]: "react developer",      // Search text
  [idTechnology]: "react",          // Selected technology
  [idLocation]: "remoto",           // Selected location
  [idExperienceLevel]: "senior"     // Selected experience level
}
Note: The field names are generated using React’s useId() hook for uniqueness.

Styling

The component uses global CSS classes:
  • jobs-search - Main section container
  • search-bar - Search input container
  • search-filters - Filter dropdowns container
  • clear-input-button - Clear button styling

Usage Patterns

import { SearchFormSection } from '@/components'
import { useState, useEffect } from 'react'
import { useDebounce } from '@/hooks/useDebounce'

function RealTimeSearch() {
  const [searchText, setSearchText] = useState('')
  const [filters, setFilters] = useState({})
  const debouncedSearch = useDebounce(searchText, 300)
  
  useEffect(() => {
    // Perform search when debounced value changes
    performSearch(debouncedSearch, filters)
  }, [debouncedSearch, filters])
  
  return (
    <SearchFormSection
      onTextFilter={setSearchText}
      onSearch={setFilters}
      initialTextInput={searchText}
    />
  )
}

With URL Parameters

import { SearchFormSection } from '@/components'
import { useSearchParams } from 'react-router-dom'

function SearchWithUrl() {
  const [searchParams, setSearchParams] = useSearchParams()
  
  const handleSearch = (formData) => {
    // Convert FormData to URLSearchParams
    const params = {}
    for (let [key, value] of formData.entries()) {
      if (value) params[key] = value
    }
    setSearchParams(params)
  }
  
  return (
    <SearchFormSection
      onTextFilter={() => {}}
      onSearch={handleSearch}
      initialTextInput={searchParams.get('search') || ''}
    />
  )
}

Filter Job Listings

function filterJobs(jobs, formData, textQuery) {
  return jobs.filter(job => {
    // Text search
    if (textQuery) {
      const searchableText = `
        ${job.titulo} 
        ${job.empresa} 
        ${job.descripcion}
      `.toLowerCase()
      
      if (!searchableText.includes(textQuery.toLowerCase())) {
        return false
      }
    }
    
    // Technology filter
    const technology = formData.get('technology')
    if (technology && job.data.technology !== technology) {
      return false
    }
    
    // Location filter
    const location = formData.get('location')
    if (location && job.ubicacion !== location) {
      return false
    }
    
    // Experience level filter
    const level = formData.get('experienceLevel')
    if (level && job.data.nivel !== level) {
      return false
    }
    
    return true
  })
}

Accessibility

  • Uses semantic <section> element
  • Form has role="search" for screen readers
  • Clear button has aria-label="Clear search input"
  • Input has descriptive placeholder text
  • All form controls have associated labels (via useId)

Source Code

Location: src/components/SearchFormSection/SearchFormSection.jsx:4

Dependencies

  • react - For useId hook
  • @/hooks/useSearchForm - Custom hook for form state management

JobListings

Display filtered job results

Pagination

Paginate search results

Best Practices

  1. Debounce text input - Avoid excessive filtering on every keystroke
  2. Persist filters - Save filter state to URL parameters for shareable links
  3. Show result count - Display how many jobs match the current filters
  4. Clear feedback - Indicate when filters are active
  5. Handle empty results - Use JobListings component which handles empty states
  6. Mobile responsive - Ensure filters work well on small screens
  7. Loading states - Show loading indicator while filtering large datasets

Build docs developers (and LLMs) love