Design with AI
React for Designers and PMs

Search contacts

A React component that filters a list of contacts by name or number as you type in a search input.

LLM Written
Human Reviewed

Output

Type here to search

Alice

123-456-7890

Bob

987-654-3210

Charlie

555-555-5555

David

111-222-3333

Eve

444-444-4444

Frank

666-666-6666

File Structure

├── src
`  `├── App.tsx `← we are here`
`  `├── index.css
`  `└── main.tsx

Distraction free code

const [contacts, setContacts] = useState([
  { id: 1, name: 'Alice', number: '123-456-7890', isFav: true },
  { id: 2, name: 'Bob', number: '987-654-3210', isFav: false },
  ...
]);
const [search, setSearch] = useState('');

const filteredContacts = contacts.filter(
  (contact) =>
    contact.name.toLowerCase().includes(search.toLowerCase()) ||
    contact.number.includes(search)
);

{/*Search input*/}
<input
  value={search}
  onChange={(e) => setSearch(e.target.value)}
/>
{/*Empty state*/}
{filteredContacts.length === 0 && <p>No contacts found</p>}
{/*Filtered list*/}
{filteredContacts.map((contact) => (
  <div key={contact.id}>
    <p>{contact.name}</p>
    <p>{contact.number}</p>
    {contact.isFav && <Star />}
  </div>
))}

Code

'use client';
import { Search, User, Star } from 'lucide-react';
import { useState } from 'react';

export default function Home() {
  const [contacts, setContacts] = useState([
    { id: 1, name: 'Alice', number: '123-456-7890', isFav: true },
    { id: 2, name: 'Bob', number: '987-654-3210', isFav: false },
    { id: 3, name: 'Charlie', number: '555-555-5555', isFav: false },
    { id: 4, name: 'David', number: '111-222-3333', isFav: true },
    { id: 5, name: 'Eve', number: '444-444-4444', isFav: false },
    { id: 6, name: 'Frank', number: '666-666-6666', isFav: false },
  ]);

  const [search, setSearch] = useState('');

  const filteredContacts = contacts.filter(
    (contact) =>
      contact.name.toLowerCase().includes(search.toLowerCase()) ||
      contact.number.includes(search)
  );

  return (
    <>
      <div className='flex items-center gap-2 bg-white border border-gray-100 px-4 py-3'>
        <Search size={18} className='text-gray-400' />
        <input
          type='text'
          placeholder='Search by name or number...'
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          className='w-full outline-none text-sm text-gray-700 placeholder-gray-400'
        />
      </div>
      {filteredContacts.length === 0 && (
        <p className='text-gray-400 text-sm p-6'>No contacts found</p>
      )}
      {filteredContacts.map((contact) => (
        <div key={contact.id} className='flex items-center gap-4 bg-white border border-gray-100 px-4'>
          <User size={24} className='text-blue-500' />
          <p className='py-6'>{contact.name}</p>
          <p className='text-gray-500'>{contact.number}</p>
          {contact.isFav && <Star size={20} className='fill-orange-300 stroke-orange-400' />}
        </div>
      ))}
    </>
  );
}

Layer Visualisation

Layers

Approximation of how this code would look in Figma's layer panel.

Home
ReactImport
IconImport
contacts"[{ id, name, number, isFav }, ...]"
search"''"
filteredContacts
contactsData"[{ id: 1, name: 'Alice', isFav: true }, ...]"
SearchContactsFragment
SearchBar
SearchIcon
SearchInput
showEmptyStateIfNoResults
EmptyStateMessage"No contacts found"
ContactRow
UserIcon
ContactName"{contact.name}"
ContactNumber"{contact.number}"
showStarIfFavourite
FavouriteStarIcon
Select a layer to inspect

What's new and what's happening?

New!

New concepts: controlled input with onChange, callbacks, derived/filtered data, .filter() + .includes() for searching, and rendering a filtered list.

On this page