The SearchAddress component provides a flexible and interactive search interface for addresses, utilizing the powerful Nominatim service from OpenStreetMap. This component is built with React and integrates UI components from shadcn-ui and icons from lucide-react.

leaflet-geosearch to support multiple geocoding providers.First, ensure that shadcn-ui components are added to your project. If not already installed, you can add them using the following command:
npx shadcn-ui@latest add button command popover
Additionally, install lucide-react for using icons:
npm install lucide-react
Or using yarn:
yarn add lucide-react
Next, install leaflet-geosearch for geocoding support:
npm install leaflet-geosearch
Or using yarn:
yarn add leaflet-geosearch
Copy the component file into your project:
search-address.tsx from the repository.components/ui/.Copy the hook files into your project:
use-debounce.ts and use-search-address.ts from the repository.hooks/.Include the SearchAddress component in your React application by importing it:
```javascript import dynamic from "next/dynamic";
const SearchAddress = dynamic(() => import("@/components/ui/search-address"), { ssr: false, }); ```
Using dynamic imports with SSR disabled helps avoid the window is not defined error during server-side rendering.
You can use the SearchAddress component anywhere within your React application:
```jsx function App() { return (
<SearchAddress onSelectLocation={(location) => console.log(location)} />
);
} ```
useDebounceThis hook debounces a value, improving performance by limiting the number of times a function is called.
import * as React from "react";
export function useDebounce<T>(value: T, delay?: number): T {
const [debouncedValue, setDebouncedValue] = React.useState<T>(value);
React.useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay ?? 500);
return () => {
clearTimeout(timer);
};
}, [value, delay]);
return debouncedValue;
}
useSearchAddressThis hook encapsulates the search logic and state management for the SearchAddress component.
import { useState, useCallback, useEffect } from "react";
import { OpenStreetMapProvider } from "leaflet-geosearch";
import { RawResult } from "leaflet-geosearch/dist/providers/bingProvider.js";
import { SearchResult } from "leaflet-geosearch/dist/providers/provider.js";
import { useDebounce } from "./use-debounce";
interface UseSearchAddressResult {
query: string;
results: Record<string, SearchResult<RawResult>[]>;
loading: boolean;
handleSearch: (value: string) => void;
selectedItem: SearchResult<RawResult> | null;
setSelectedItem: (item: SearchResult<RawResult> | null) => void;
}
export const useSearchAddress = (): UseSearchAddressResult => {
const [query, setQuery] = useState("");
const [results, setResults] = useState<
Record<string, SearchResult<RawResult>[]>
>({});
const [loading, setLoading] = useState(false);
const [selectedItem, setSelectedItem] =
useState<SearchResult<RawResult> | null>(null);
const debouncedQuery = useDebounce(query, 500);
const groupByType = useCallback(
(data: SearchResult<RawResult>[]): Record<string, SearchResult<RawResult>[]> => {
return data.reduce(
(acc, item) => {
const { raw } = item;
const rawData = raw as unknown as any;
const type = rawData.class;
if (!acc[type]) {
acc[type] = [];
}
acc[type]?.push(item);
return acc;
},
{} as Record<string, SearchResult<RawResult>[]>
);
},
[]
);
const handleSearch = (value: string) => {
setQuery(value);
};
useEffect(() => {
const fetchResults = async () => {
if (debouncedQuery.length > 2) {
setLoading(true);
const provider = new OpenStreetMapProvider();
const results = await provider.search({ query: debouncedQuery });
setResults(groupByType(results as unknown as SearchResult<RawResult>[]));
setLoading(false);
} else {
setResults({});
}
};
fetchResults();
}, [debouncedQuery, groupByType]);
return {
query,
results,
loading,
handleSearch,
selectedItem,
setSelectedItem,
};
};
This component relies on the following external libraries and styles:
shadcn-ui for UI components such as Button, Command, and Popover.lucide-react for icons like Check and ChevronsUpDown.leaflet-geosearch for geocoding support with multiple providers.The component makes requests to the https://nominatim.openstreetmap.org/search endpoint. Ensure that any API usage complies with the Nominatim usage policy, particularly regarding the custom HTTP header requirements for identification.
For configuration options and details on supported providers, refer to the leaflet-geosearch documentation.
Contributions to enhance or fix issues in the SearchAddress component are welcome. Please follow the standard pull request process for this repository.
This component is available under the MIT License. See the LICENSE file in the repository for full license text.
$ claude mcp add search-address \
-- python -m otcore.mcp_server <graph>