21st

ReUI Filters

Component 2

About

ReUI Filters with the first 10 official ReUI examples, preserved from the MIT-licensed source registry.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds ReUI Filters to your project and fits it to your theme.

Code

How ReUI Filters is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import { AtSignIcon, GlobeIcon, PhoneIcon, UserIcon, CreditCardIcon, LinkIcon, ListFilterIcon } from "lucide-react"
"use client"

import { useCallback, useState } from "react"
import {
  createFilter,
  Filters,
  type Filter,
  type FilterFieldConfig,
} from "@/components/ui/reui-filters"
import { z } from "zod"

import { Button } from "@/components/ui/reui-filters-utils/button"

// Zod validation helper - wraps a Zod schema to return validation result with message
function zodValidator<T extends z.ZodType>(schema: T) {
  return (value: unknown): { valid: boolean; message?: string } => {
    const result = schema.safeParse(value)
    if (result.success) {
      return { valid: true }
    }
    // Get the first error message from Zod using format()
    const formatted = result.error.format()
    const message =
      formatted._errors?.[0] || result.error.message || "Invalid value"
    return { valid: false, message }
  }
}

// Define Zod schemas for different field types
const emailSchema = z
  .string()
  .min(1, { message: "Email is required" })
  .pipe(z.email({ message: "Please enter a valid email address" }))

const urlSchema = z
  .string()
  .pipe(
    z.url({ message: "Please enter a valid URL (e.g., https://example.com)" })
  )

const phoneSchema = z
  .string()
  .regex(/^\+?[1-9]\d{1,14}$/, { message: "Please enter a valid phone number" })

const usernameSchema = z
  .string()
  .min(3, { message: "Username must be at least 3 characters" })
  .max(20, { message: "Username must be at most 20 characters" })
  .regex(/^[a-zA-Z0-9_]+$/, {
    message: "Username can only contain letters, numbers, and underscores",
  })

const creditCardSchema = z.string().regex(/^\d{13,19}$/, {
  message: "Please enter a valid credit card number (13-19 digits)",
})

export default function Pattern() {
  const fields: FilterFieldConfig[] = [
    {
      key: "email",
      label: "Email",
      icon: (
        <AtSignIcon className="size-3.5" />
      ),
      type: "text",
      placeholder: "user@example.com",
      // Use Zod validator for email validation
      validation: zodValidator(emailSchema),
    },
    {
      key: "website",
      label: "Website",
      icon: (
        <GlobeIcon className="size-3.5" />
      ),
      type: "text",
      placeholder: "https://example.com",
      // Use Zod validator for URL validation
      validation: zodValidator(urlSchema),
    },
    {
      key: "phone",
      label: "Phone",
      icon: (
        <PhoneIcon className="size-3.5" />
      ),
      type: "text",
      placeholder: "+1234567890",
      // Use Zod validator for phone validation
      validation: zodValidator(phoneSchema),
    },
    {
      key: "username",
      label: "Username",
      icon: (
        <UserIcon className="size-3.5" />
      ),
      type: "text",
      className: "w-44",
      placeholder: "john_doe",
      // Use Zod validator for username validation
      validation: zodValidator(usernameSchema),
    },
    {
      key: "cardNumber",
      label: "Card Number",
      icon: (
        <CreditCardIcon className="size-3.5" />
      ),
      type: "text",
      placeholder: "4111111111111111",
      // Use Zod validator for credit card validation
      validation: zodValidator(creditCardSchema),
    },
    {
      key: "customUrl",
      label: "Custom URL",
      icon: (
        <LinkIcon className="size-3.5" />
      ),
      type: "text",
      placeholder: "https://...",
      // Custom validation function without Zod (for comparison)
      validation: (value) => {
        const urlPattern = /^https?:\/\/.+\..+/
        if (!urlPattern.test(value as string)) {
          return {
            valid: false,
            message: "URL must start with http:// or https://",
          }
        }
        return { valid: true }
      },
    },
  ]

  const [filters, setFilters] = useState<Filter[]>([
    createFilter("email", "contains", [""]),
  ])

  const handleFiltersChange = useCallback((filters: Filter[]) => {
    setFilters(filters)
  }, [])

  return (
    <div className="flex w-full max-w-[850px] items-start">
      <Filters
        filters={filters}
        fields={fields}
        trigger={
          <Button variant="outline" size="icon">
            <ListFilterIcon />
          </Button>
        }
        onChange={handleFiltersChange}
      />
    </div>
  )
}

Dependencies

More by 21st

View all
  • Futuristic Hero

    Futuristic Hero

    2.7K
  • Hero ASCII one

    Hero ASCII one

    1.4K
  • Hero ASCII

    Hero ASCII

    1.3K
  • Admit One Ticket

    Admit One Ticket

    961
  • Navigation Menu

    Navigation Menu

    916
  • Border Beam

    Border Beam

    890
  • Border Beam Search

    Border Beam Search

    545
  • Thinking Orbs

    Thinking Orbs

    438