iconiq.

Contributing Code

To contribute to the Iconiq library, follow the steps below.

Important

Iconiq only accepts contributions based on Lucide icons. Pull requests containing custom icons or icons from other packs will be closed.

Animation quality: PRs with simple path length animations (strokeDasharray/strokeDashoffset "drawing" effect) will likely be rejected. We want creative, purposeful animations that enhance the icon.

We welcome contributions. Please follow these steps:

1. Fork and clone

Fork the repository on GitHub, then clone your fork:

text
git clone https://github.com/edwinvakayil/iconiq.git

Navigate to the project and create a branch:

text
cd iconiq
git checkout -b your-branch-name

2. Install dependencies

Iconiq uses pnpm:

text
pnpm install

3. Create your animated icon

  • In /icons/, create a new file with the icon name in lowercase and hyphens (e.g. heart-icon.tsx, arrow-up.tsx).
  • Use the template below. Replace [YourIconName] with your icon name in PascalCase, and replace the SVG comment with the path from lucide.dev.
  • Add your animation logic using Framer Motion's motion components and the controls object.
text
'use client';

import { useAnimation } from 'motion/react';
import type { HTMLAttributes } from 'react';
import { forwardRef, useCallback, useImperativeHandle, useRef } from 'react';
import { cn } from '@/lib/utils';

export interface [YourIconName]IconHandle {
  startAnimation: () => void;
  stopAnimation: () => void;
}

interface [YourIconName]IconProps extends HTMLAttributes<HTMLDivElement> {
  size?: number;
}

const [YourIconName]Icon = forwardRef<[YourIconName]IconHandle, [YourIconName]IconProps>(
  ({ onMouseEnter, onMouseLeave, className, size = 28, ...props }, ref) => {
    const controls = useAnimation();
    const isControlledRef = useRef(false);

    useImperativeHandle(ref, () => {
      isControlledRef.current = true;
      return {
        startAnimation: () => controls.start('animate'),
        stopAnimation: () => controls.start('normal'),
      };
    });

    const handleMouseEnter = useCallback(
      (e: React.MouseEvent<HTMLDivElement>) => {
        if (!isControlledRef.current) {
          controls.start('animate');
        } else {
          onMouseEnter?.(e);
        }
      },
      [controls, onMouseEnter]
    );

    const handleMouseLeave = useCallback(
      (e: React.MouseEvent<HTMLDivElement>) => {
        if (!isControlledRef.current) {
          controls.start('normal');
        } else {
          onMouseLeave?.(e);
        }
      },
      [controls, onMouseLeave]
    );

    return (
      <div
        className={cn(className)}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        {...props}
      >
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width={size}
          height={size}
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          strokeWidth="2"
          strokeLinecap="round"
          strokeLinejoin="round"
        >
          {/* your svg code here */}
        </svg>
      </div>
    );
  }
);

[YourIconName]Icon.displayName = '[YourIconName]Icon';

export { [YourIconName]Icon };

4. Add your icon to the icon list

Open icons/index.ts. Import your icon and add it to the top of ICON_LIST:

text
import { [YourIconName]Icon } from './[icon-name]';

// Add at the top of ICON_LIST:
{
  name: '[icon-name]',
  icon: [YourIconName]Icon,
  keywords: ['keyword1', 'keyword2', 'keyword3'],
},

Use the exact name and keywords from lucide.dev for your icon.

5. Update the registry

For new icons, run:

text
pnpm run gen-cli

This syncs your icon to the registry for the shadcn CLI.

6. Build and test

text
pnpm build
pnpm lint

7. Commit and open a pull request

text
git commit -m "Add [icon-name] animated icon"
git push origin your-branch-name

Open a pull request on the original repository with a clear description of the icon and animation.

Thank you for contributing to Iconiq!