# Filterable Jobs Board

> Filterable jobs board with a search field, department filter pills, a live result count, and a scrollable list of roles with location and type.

A shadcn/ui block from the [@7ovr](https://7ovr.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/7ovr/careers-4).

- Page, with a live preview: https://shadcn.exchange/r/7ovr/careers-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/7ovr/careers-4-light-42fd28b04e500ec6.png
- Registry item: https://7ovr.com/r/careers-4.json
- Categories: careers
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://7ovr.com/r/careers-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@base-ui/react`
- Registry items: `badge`, `input`, `scroll-area`

## Files

### `registry/blocks/careers/4/careers-block.tsx`

```tsx
"use client"

import { useMemo, useState } from "react"
import { cn } from "@/lib/utils"
import { Badge } from "@/components/ui/badge"
import { Input } from "@/components/ui/input"
import { ScrollArea } from "@/components/ui/scroll-area"
import { IconPlaceholder } from "@/components/icons/icon-placeholder"

type Job = {
  title: string
  department: string
  location: string
  type: string
}

const jobs: Job[] = [
  {
    title: "Senior Frontend Engineer",
    department: "Engineering",
    location: "Remote",
    type: "Full-time",
  },
  {
    title: "Backend Engineer, Platform",
    department: "Engineering",
    location: "Berlin",
    type: "Full-time",
  },
  {
    title: "Product Designer",
    department: "Design",
    location: "Remote",
    type: "Full-time",
  },
  {
    title: "Design Systems Lead",
    department: "Design",
    location: "London",
    type: "Full-time",
  },
  {
    title: "Growth Marketing Manager",
    department: "Marketing",
    location: "Remote",
    type: "Full-time",
  },
  {
    title: "Content Strategist",
    department: "Marketing",
    location: "Remote",
    type: "Contract",
  },
  {
    title: "Customer Success Manager",
    department: "Operations",
    location: "New York",
    type: "Full-time",
  },
]

const departments = ["All", "Engineering", "Design", "Marketing", "Operations"]

export default function CareersBlock() {
  const [query, setQuery] = useState("")
  const [department, setDepartment] = useState("All")

  const results = useMemo(() => {
    const q = query.trim().toLowerCase()
    return jobs.filter((job) => {
      const matchesQuery =
        !q ||
        job.title.toLowerCase().includes(q) ||
        job.location.toLowerCase().includes(q)
      const matchesDept = department === "All" || job.department === department
      return matchesQuery && matchesDept
    })
  }, [query, department])

  return (
    <section className="w-full bg-background px-6 py-16 text-foreground">
      <div className="mx-auto w-full max-w-3xl">
        <div className="flex flex-col gap-2">
          <p className="text-xs font-semibold tracking-widest text-muted-foreground uppercase">
            Careers
          </p>
          <h1 className="font-heading text-3xl font-bold tracking-tight">
            Open positions
          </h1>
          <p className="text-sm text-muted-foreground">
            Join us in building tools teams love. {jobs.length} roles open
            across the company.
          </p>
        </div>

        <div className="mt-8 flex flex-col gap-4">
          <div className="relative">
            <IconPlaceholder
              lucide="Search"
              tabler="IconSearch"
              hugeicons="SearchIcon"
              phosphor="MagnifyingGlass"
              remixicon="RiSearchLine"
              className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground"
              aria-hidden="true"
            />
            <Input
              type="search"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder="Search by title or location..."
              className="pl-8"
              aria-label="Search jobs"
            />
          </div>
          <div
            className="flex flex-wrap gap-1.5"
            role="group"
            aria-label="Filter by department"
          >
            {departments.map((dept) => (
              <button
                key={dept}
                type="button"
                aria-pressed={department === dept}
                onClick={() => setDepartment(dept)}
                className={cn(
                  "rounded-md border px-3 py-1 text-xs font-medium transition-colors",
                  department === dept
                    ? "border-foreground bg-foreground text-background"
                    : "border-border text-muted-foreground hover:bg-muted/60 hover:text-foreground"
                )}
              >
                {dept}
              </button>
            ))}
          </div>
        </div>

        <p
          className="mt-6 text-xs text-muted-foreground"
          role="status"
          aria-live="polite"
        >
          <span className="font-medium text-foreground tabular-nums">
            {results.length}
          </span>{" "}
          {results.length === 1 ? "role" : "roles"}
        </p>

        <ScrollArea className="mt-2 h-96 border-y border-border [&_[data-slot=scroll-area-viewport]]:scroll-fade-y">
          <ul className="flex flex-col divide-y divide-border">
            {results.map((job) => (
              <li key={job.title}>
                <a
                  href="#"
                  className="group flex items-center justify-between gap-4 py-4 pr-3"
                >
                  <div className="flex min-w-0 flex-col gap-1">
                    <span className="font-medium">{job.title}</span>
                    <span className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
                      <span>{job.department}</span>
                      <span className="inline-flex items-center gap-1">
                        <IconPlaceholder
                          lucide="MapPin"
                          tabler="IconMapPin2"
                          hugeicons="MapPinIcon"
                          phosphor="MapPin"
                          remixicon="RiMapPin2Line"
                          className="size-3.5"
                          aria-hidden="true"
                        />
                        {job.location}
                      </span>
                      <Badge variant="secondary" className="font-normal">
                        {job.type}
                      </Badge>
                    </span>
                  </div>
                  <IconPlaceholder
                    lucide="ArrowRight"
                    tabler="IconArrowRight"
                    hugeicons="ArrowRightIcon"
                    phosphor="ArrowRight"
                    remixicon="RiArrowRightLine"
                    className="size-4 shrink-0 text-muted-foreground transition-transform group-hover:translate-x-0.5"
                    aria-hidden="true"
                  />
                </a>
              </li>
            ))}
            {results.length === 0 && (
              <li className="py-10 text-center text-sm text-muted-foreground">
                No roles match your search.
              </li>
            )}
          </ul>
        </ScrollArea>
      </div>
    </section>
  )
}
```
