Skip to main content
The Menu component displays a searchable list of countries organized by region. It includes a search bar, loading states, and a fullscreen toggle for mobile devices.

Overview

The Menu component:
  • Organizes countries by geographic region
  • Provides search functionality to filter countries
  • Displays progress indicators for each region
  • Supports fullscreen mode on mobile devices
  • Shows loading and empty states

Props

interface Props {
  loading?: boolean | undefined;
  fullscreen: boolean;
  toggleFullscreen: () => void;
}
loading
boolean
default:"false"
Controls whether the loading spinner is displayed
fullscreen
boolean
required
Current fullscreen state of the menu on mobile devices
toggleFullscreen
() => void
required
Callback function to toggle fullscreen mode on mobile

Component Signature

export const Menu: FC<Props> = memo(
  ({ loading = false, fullscreen, toggleFullscreen }) => {
    // Component implementation
  }
);

Features

Search Functionality

The menu includes a search input that filters countries by name:
const [search, setSearch] = useState('');
const filteredRegions = useMemo(() => {
  const s = search.trim().toLowerCase();
  if (!s) {
    return regions;
  }
  return regions
    .map((region) => ({
      ...region,
      values: region.values.filter(({ name }) =>
        name.toLowerCase().includes(s),
      ),
    }))
    .filter((region) => region.values.length > 0);
}, [search, regions]);

Regional Organization

Countries are organized by region with sticky headers:
{filteredRegions.map((region) => (
  <li key={region.name}>
    <div className="sticky top-0 bg-zinc-200 dark:bg-zinc-800">
      <h2>{region.name || 'Other'}</h2>
      <Progress complete={region.complete ?? 0} />
    </div>
    <ul>
      {region.values.map((country) => (
        <MenuItem key={country.iso3166} country={country} />
      ))}
    </ul>
  </li>
))}

Mobile Fullscreen Toggle

On mobile devices, a button allows expanding the menu to fullscreen:
<button
  type="button"
  className="visible p-2 md:hidden"
  onClick={toggleFullscreen}
>
  {fullscreen ? <ContractIcon /> : <ExpandIcon />}
</button>

States

Loading State

Displays an animated spinner while data is loading:
{loading ? (
  <div className="flex size-full items-center justify-center">
    <svg className="size-5 animate-spin">
      {/* Spinner SVG */}
    </svg>
  </div>
) : (
  // Menu content
)}

Empty State

Shows a message when search returns no results:
{filteredRegions.length === 0 ? (
  <div className="m-4 h-full text-center font-medium">
    No results!
  </div>
) : (
  // Country list
)}

Usage Example

import { Menu } from './components/menu';
import { useState } from 'react';

function App() {
  const [loading, setLoading] = useState(false);
  const [fullscreen, setFullscreen] = useState(false);

  return (
    <Menu
      loading={loading}
      fullscreen={fullscreen}
      toggleFullscreen={() => setFullscreen(!fullscreen)}
    />
  );
}

Layout Structure

The menu consists of three main sections:
  1. Search Bar - Text input with optional fullscreen toggle
  2. Loading/Empty State - Conditional rendering based on state
  3. Country List - Scrollable list organized by region
<>
  <div className="flex border-t-2">
    <form>Search input</form>
    <button>Fullscreen toggle</button>
  </div>
  
  {loading ? (
    <LoadingSpinner />
  ) : filteredRegions.length === 0 ? (
    <EmptyState />
  ) : (
    <CountryList />
  )}
</>

Accessibility

  • Search input has a proper <label> with sr-only class for screen readers
  • SVG icons include <title> elements
  • Semantic HTML structure with proper heading hierarchy
<label htmlFor={searchId} className="sr-only">
  Search
</label>
<input id={searchId} type="text" placeholder="Search..." />

Styling

The menu uses Tailwind CSS with responsive design:
  • Mobile-first approach with md: breakpoint modifiers
  • Dark mode support with dark: variant
  • Sticky region headers that remain visible during scroll
  • Smooth hover effects on country items

State Integration

The Menu component uses the regionsAtom from Jotai to access the organized country data:
const regions = useAtomValue(regionsAtom);

Dependencies

  • jotai - State management
  • react - Core React functionality
  • MenuItem - Child component for individual countries
  • Progress - Progress indicator component

Source Code

Location: src/components/menu.tsx

Build docs developers (and LLMs) love