When working on web applications, creating an intuitive and responsive user interface is paramount. One feature that significantly enhances user experience is an autocomplete input field. In this blog post, I will guide you through creating a zip code autocomplete component using Next.js 14, Mantine, and TypeScript. This component will dynamically fetch suggestions from an API as the user types, ensuring a seamless experience.
Thank me by sharing on Twitter 🙏
Introduction
Autocomplete inputs are incredibly useful for improving form usability. They help users quickly find and select from a list of suggestions, reducing the chance of errors and speeding up the input process. By integrating Mantine’s Autocomplete
component with a custom Next.js API route, we can create a robust and efficient zip code autocomplete feature.
Setting Up the Next.js API Route
First, let’s create an API route in our Next.js 14 application to handle requests for zip codes. This API will simulate fetching data from a database or an external service.
Here’s the code for the API route:
// app/api/zipcodes/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const query = searchParams.get('query') || '';
// Simulate fetching data from a database or external API
const zipcodes = ['12345', '23456', '34567', '45678', '56789'];
const filteredZipcodes = zipcodes.filter((zipcode) =>
zipcode.startsWith(query)
);
return NextResponse.json(filteredZipcodes);
}
This code defines a GET handler for the /api/zipcodes
route. It retrieves the query
parameter from the request URL, filters a predefined list of zip codes, and returns the filtered results as a JSON response.
SanDisk 128GB Extreme PRO SDXC UHS-I Memory Card - C10, U3, V30, 4K UHD, SD Card - SDSDXXD-128G-GN4IN
$19.99 (as of December 21, 2024 08:38 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Pokémon 2025 Wall Calendar
$12.47 (as of December 21, 2024 19:39 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Super Mario 2025 Wall Calendar
$11.60 (as of December 21, 2024 19:39 GMT +00:00 - More infoProduct prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on [relevant Amazon Site(s), as applicable] at the time of purchase will apply to the purchase of this product.)Creating the Zip Code Autocomplete Component
Next, let’s create the ZipcodeAutocomplete
component. This component will extend Mantine’s Autocomplete
component and fetch zip code suggestions from our API as the user types. We will also ensure that the input is restricted to numerical values only.
Here’s the TypeScript code for the component:
// components/ZipcodeAutocomplete.tsx
'use client';
import { useState, useEffect } from 'react';
import { Autocomplete, AutocompleteProps } from '@mantine/core';
import { useDebouncedValue } from '@mantine/hooks';
// Extend the props of Autocomplete
type ZipcodeAutocompleteProps = Omit<AutocompleteProps, 'data'>;
export const ZipcodeAutocomplete: React.FC<ZipcodeAutocompleteProps> = (props) => {
const [value, setValue] = useState<string>('');
const [data, setData] = useState<string[]>([]);
const [debouncedValue] = useDebouncedValue(value, 300);
useEffect(() => {
if (debouncedValue.trim().length === 0) {
setData([]);
return;
}
if (/^\d*$/.test(debouncedValue)) {
fetch(`/api/zipcodes?query=${debouncedValue}`)
.then((response) => response.json())
.then((result: string[]) => setData(result));
}
}, [debouncedValue]);
const handleChange = (val: string) => {
if (/^\d*$/.test(val)) {
setValue(val);
}
};
return (
<Autocomplete
{...props}
value={value}
onChange={handleChange}
data={data}
label="Zipcode"
placeholder="Enter zipcode"
/>
);
};
In this component, I used the useDebouncedValue
hook from Mantine to debounce the user input, preventing excessive API calls. The handleChange
function ensures that only numerical values are accepted, using a regular expression to validate the input.
Using the ZipcodeAutocomplete Component
Now that we have our ZipcodeAutocomplete
component, let’s integrate it into our main page. This will allow users to start typing a zip code and see autocomplete suggestions in real-time.
Here’s how you can include the component in a Next.js page:
// app/page.tsx
import { ZipcodeAutocomplete } from '../components/ZipcodeAutocomplete';
const Home: React.FC = () => {
return (
<div>
<h1>Find Contractors</h1>
<ZipcodeAutocomplete
placeholder="Enter your zipcode"
label="Zipcode"
description="Type to search for zipcodes"
/>
</div>
);
};
export default Home;
This simple integration demonstrates how the ZipcodeAutocomplete
component can be used in a real application. Users will see a labeled input field where they can start typing a zip code, and suggestions will appear dynamically based on their input.
Conclusion
Creating a zip code autocomplete component in a Next.js application using Mantine and TypeScript is straightforward and significantly enhances user experience. By leveraging Mantine’s Autocomplete
component and integrating it with a custom API route, we can provide users with a responsive and efficient input field that dynamically fetches suggestions.
This approach not only improves usability but also ensures that the input is validated and restricted to numerical values, reducing the likelihood of errors. Whether you’re building a small project or a large application, adding features like autocomplete can make a big difference in how users interact with your forms.
I hope this guide helps you implement a similar feature in your projects. Happy coding!