React Icon Picker: Lightweight, Customizable, and Built with ShadCN, TailwindCSS

Discover a modern React Icon Picker built with ShadCN components and TailwindCSS. Perfect for Next.js projects, it features search, customization, and seamless integration. Add it to your UI today!

Ryan Mogk

December 7th, 2024

16 likes

3180 views

Looking for a React icon picker that’s flexible, developer-friendly, and built with modern technologies like ShadCN and TailwindCSS? Meet the React Icon Picker—a dynamic, reusable component designed to integrate seamlessly into your Next.js or React projects.

Whether you’re creating a CMS, admin panel, or design system, this Icon Picker simplifies the process of selecting and managing icons, offering a clean, intuitive interface with built-in search and customizable design.

The icon picker is built of 2 main components,

  1. A hook for collecting icon data useIconPicker()

    • Has built in search that can be removed depending on use case

  2. A component for rendering the icon <IconRenderer icon="..." />

    • Pass the icon name to render your icon. Accepts all the same props your icon component would.

We've got some demo's below with popular UI components but the useIconPicker() hook & <IconRenderer /> component are all you need to build your own from scratch.

Getting Started With The Icon Picker

Before you use this in your project, you'll need to have TailwindCSS installed in your project, & our example uses Hero Icons.

Hero icons install

npm i @heroicons/react

Installing the Icon Picker in Your Project

Copy the code below directly into your project to get started with the icon picker.

Client Side With Controlled Filtering

Controlled Component

"use client";
import React, { useMemo, useState } from "react";
import * as HeroIcons from "@heroicons/react/20/solid";
type Icons = {
  // the name of the component
  name: string;
  // a more human-friendly name
  friendly_name: string;
  Component: React.FC<React.ComponentPropsWithoutRef<"svg">>;
};

export const useIconPicker = (): {
  search: string;
  setSearch: React.Dispatch<React.SetStateAction<string>>;
  icons: Icons[];
} => {

  const icons: Icons[] = useMemo(
    () =>
      Object.entries(HeroIcons).map(([iconName, IconComponent]) => ({
        name: iconName,
        // split the icon name at capital letters and join them with a space
        friendly_name: iconName.match(/[A-Z][a-z]+/g)?.join(" ") ?? iconName,
        Component: IconComponent,
      })),
    [],
  );

  // these lines can be removed entirely if you're not using the controlled component approach
  const [search, setSearch] = useState("");
  //   memoize the search functionality
  const filteredIcons = useMemo(() => {
    return icons.filter((icon) => {
      if (search === "") {
        return true;
      } else if (icon.name.toLowerCase().includes(search.toLowerCase())) {
        return true;
      } else {
        return false;
      }
    });
  }, [icons, search]);

  return { search, setSearch, icons: filteredIcons };
};

export const IconRenderer = ({
  icon,
  ...rest
}: {
  icon: string;
} & React.ComponentPropsWithoutRef<"svg">) => {
  const IconComponent = HeroIcons[icon as keyof typeof HeroIcons];

  if (!IconComponent) {
    return null;
  }

  return <IconComponent data-slot="icon" {...rest} />;
};

Server Compatible Icon Picker

Server Compatible

import React from "react";
import * as HeroIcons from "@heroicons/react/20/solid";

type Icons = {
  // the name of the component
  name: string;
  // a more human-friendly name
  friendly_name: string;
  Component: React.FC<React.ComponentPropsWithoutRef<"svg">>;
};

export const useIconPicker = (): {
  icons: Icons[];
} => {
  return {
    icons: Object.entries(HeroIcons).map(([iconName, IconComponent]) => ({
      name: iconName,
      // split the icon name at capital letters and join them with a space
      friendly_name: iconName.match(/[A-Z][a-z]+/g)?.join(" ") ?? iconName,
      Component: IconComponent,
    })),
  };
};

export const IconRenderer = ({
  icon,
  ...rest
}: {
  icon: string;
} & React.ComponentPropsWithoutRef<"svg">) => {
  const IconComponent = HeroIcons[icon as keyof typeof HeroIcons];

  if (!IconComponent) {
    return null;
  }

  return <IconComponent data-slot="icon" {...rest} />;
};

React Icon Picker Component Examples

ShadCN Command Icon Picker

This example uses the shadCN popover, and shadCN command components.

Command Palette Icon Picker

ShadCN Dialog Icon Picker

This example uses the shadCN dialog, shadCN button, and shadCN input components.

ShadCN Dialog Icon Picker

TailwindCSS Catalyst Icon Picker Component

This example uses the Catalyst components by Tailwind UI. It's a premium UI kit that we've used a lot since it's release.

Catalyst by Tailwind UI Icon Picker

React Icon Picker Dependencies

We like using hero icons, but any icon library should work. If you do use another icon library, there's a few things you should know:

  1. Icon libraries like lucide are great because they have so many icons. However, since they have so many icons you're going to run into performance issues. Implementing some pagination logic will help, but it's still a lot of data to send to the front end.

  2. Hero icons exposes the same properties that React <svg>...</svg> components do. This is not always the case depending on the icon library you're using. Make sure you update the types to match you're preferred icon library.

About the React Icon Picker

The React Icon Picker enhances your development workflow by seamlessly integrating with modern web technologies. Built with TailwindCSS for rapid UI development and customization, it ensures a responsive design across various screen sizes.

Utilizing TypeScript adds type safety, reducing common errors and improving code maintainability. This combination makes the icon picker a strong choice for developers seeking an efficient coding experience.

Incorporating Hero Icons, it provides access to a wide range of high-quality icons, enhancing user experience in modern web applications.

Tailored for Next.js, a popular React framework, it streamlines icon selection and implementation, allowing developers to focus on building exceptional user interfaces.

Overall, the React Icon Picker is essential for creating visually appealing, functional web applications, particularly within the Next.js ecosystem.

Why Aren't You Returning the SVG?

This icon picker is designed for production applications where icons need to do more than just display on the client side—most use cases also require storing icon data in a database.

Storing SVGs directly in your database is generally unnecessary and can complicate your setup. Instead, it is often more efficient and manageable to store a reference to the icon rather than the actual SVG file. This approach allows you to take full advantage of components specifically built to render SVGs efficiently.

Using references instead of embedding SVGs directly simplifies database management. It reduces the size of your database, improving performance and query times. It streamlines updates: if an SVG needs to be modified, you can update the source file without altering multiple database records.

Why We Built This Icon Picker

As developers, we wanted a solution that combined:

  • A modern design using TailwindCSS and ShadCN components.

  • A flexible API for custom integration and advanced use cases.

  • Ease of use, so developers can drop it into their projects and start building.

By making this component open-source, we aim to give back to the developer community and help others streamline their workflows.

Start Building with the React Icon Picker

Ready to add an intuitive, responsive icon picker to your project? Copy the code, customize it, and start building better user interfaces today.

If you use this component in your project, we’d greatly appreciate it if you could link back to this post or our showcase to help others discover it. Of course, there’s no obligation to do so—this component is free to use as you see fit. Your support, whether through a link, feedback, or contributions, helps us continue building tools for the community


Add a comment

This will be publicly visible.

Your email address will not be published.

Your comment will be reviewed by the admin before it is published.

Why Not Stay in the Loop?

Connect

A postcard from us a few times a year. No spam, just good vibes and updates you’ll love.

We’ll never share your email address.

Actionable Insights
Discover how custom software can streamline operations and unlock growth opportunities.
Client Stories
Be inspired by real-world success stories of businesses transforming with our software solutions.
No-Nonsense Content
We respect your inbox. Only thoughtful, high-value content—never spam.

We're a Full-Service Software Development Company

Our services

We offer a wide range of services to help you build, grow, and scale your business. Whether you need a custom website, a mobile app, or a business management system, we've got you covered.

Ready to Build the Future of Your Business?

Let's Get Started

Book a meeting, request a quote, or ask us anything. We're here to partner with you on your next big idea.