# Basic password strength with dynamic hint and icons

> Basic password strength with dynamic hint and icons

A shadcn/ui block from the [@reui](https://reui.io) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/c-input-22).

- Page, with a live preview: https://shadcn.exchange/r/reui/c-input-22
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/c-input-22-light-d12a9afd1f386cf8.png
- Registry item: https://reui.io/r/new-york-v4/c-input-22.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://reui.io/r/new-york-v4/c-input-22.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `cn`
- Registry items: `field`, `input`

## Files

### `c-input-22.tsx`

```tsx
"use client"

import { useId, useState } from "react"

import { cn } from "cn"
import { Field, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"

export function Pattern() {
  const id = useId()
  const [password, setPassword] = useState("")

  const getStrength = (pass: string) => {
    const requirements = [
      { met: pass.length >= 8, text: "8+ characters" },
      { met: /[0-9]/.test(pass), text: "a number" },
      { met: /[!@#$%^&*]/.test(pass), text: "a special character" },
    ]
    const metCount = requirements.filter((r) => r.met).length
    return { requirements, metCount }
  }

  const { metCount } = getStrength(password)

  const getHint = () => {
    if (!password) {
      return "Use 8+ characters with a number and a special character."
    }
    if (metCount === 3) {
      return "Strong password. You're all set!"
    }
    if (metCount === 2) {
      return "Almost there! Add the missing requirement."
    }
    return "Weak password. Include 8+ characters, a number, and a special character."
  }

  const getStatus = () => {
    if (!password)
      return {
        color: "text-muted-foreground",
        icon: (
          <IconPlaceholder
            lucide="InfoIcon"
            tabler="IconInfoCircle"
            hugeicons="InformationCircleIcon"
            phosphor="InfoIcon"
            remixicon="RiInformationLine"
            className="size-3.5 shrink-0"
          />
        ),
      }
    if (metCount === 3)
      return {
        color: "text-emerald-500",
        icon: (
          <IconPlaceholder
            lucide="CircleCheckIcon"
            tabler="IconCircleCheck"
            hugeicons="CheckmarkCircle01Icon"
            phosphor="CheckCircleIcon"
            remixicon="RiCheckboxCircleLine"
            className="size-3.5 shrink-0"
          />
        ),
      }
    if (metCount === 2)
      return {
        color: "text-amber-500",
        icon: (
          <IconPlaceholder
            lucide="AlertTriangleIcon"
            tabler="IconAlertTriangle"
            hugeicons="Alert02Icon"
            phosphor="WarningIcon"
            remixicon="RiAlertLine"
            className="size-3.5 shrink-0"
          />
        ),
      }
    return {
      color: "text-destructive",
      icon: (
        <IconPlaceholder
          lucide="AlertTriangleIcon"
          tabler="IconAlertTriangle"
          hugeicons="Alert02Icon"
          phosphor="WarningIcon"
          remixicon="RiAlertLine"
          className="size-3.5 shrink-0"
        />
      ),
    }
  }

  const { color, icon: StatusIcon } = getStatus()

  return (
    <Field className="w-full max-w-xs">
      <FieldLabel htmlFor={id}>Password</FieldLabel>
      <Input
        id={id}
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="Enter password"
      />
      <div
        className={cn(
          "flex items-center gap-2 text-xs transition-colors duration-200",
          color
        )}
      >
        {StatusIcon}
        <p>{getHint()}</p>
      </div>
    </Field>
  )
}
```
