npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-18-ui-library

v0.3.4

Published

A fully customizable, theme-aware React 18 enterprise UI component library

Readme

react-18-ui-library

A fully customizable, theme-aware React 18 enterprise UI component library — 70+ components, schema-driven forms, fluent validation, drag-and-drop file upload, and zero runtime CSS-in-JS overhead.

npm version Storybook TypeScript React TailwindCSS License: MIT Snyk


Story Book


Table of Contents


Installation

npm install react-18-ui-library
# or
yarn add react-18-ui-library
# or
pnpm add react-18-ui-library

Quick Start

// 1. Import the library stylesheet once at your app root
//    This single file includes:
//    - All Tailwind utility classes used by components
//    - All CSS custom property tokens (:root variables)
import 'react-18-ui-library/styles'

// 2. Import and use components
import { Button, TextField, JSONForm } from 'react-18-ui-library'

export function App() {
  return (
    <div>
      <TextField label="Name" placeholder="John Doe" />
      <Button variant="primary">Submit</Button>
    </div>
  )
}

Important: react-18-ui-library/styles must be imported before any of your own stylesheets so your CSS token overrides take precedence. You do not need Tailwind installed in your consuming app — all utility classes are pre-compiled into the library's stylesheet.

📚 View all components in Storybook → — Browse and interact with the full component library.


Theme System

All components read CSS custom properties from the parent app's :root. There are no hardcoded colors and no runtime CSS-in-JS overhead.

Override tokens in CSS

/* globals.css */
:root {
  --color-primary:            #7c3aed;
  --color-primary-hover:      #6d28d9;
  --color-primary-foreground: #ffffff;
  --color-background:         #fafafa;
  --color-surface:            #ffffff;
  --color-text:               #111827;
  --color-text-muted:         #6b7280;
  --color-border:             #e5e7eb;
  --color-error:              #ef4444;
  --color-success:            #22c55e;
  --color-warning:            #f59e0b;
  --radius-sm:                0.25rem;
  --radius-md:                0.5rem;
  --radius-lg:                0.75rem;
  --font-family-base:         'Inter', sans-serif;
  --navbar-height:            64px;
  --sidebar-width:            260px;
  --sidebar-collapsed-width:  64px;
}

Or use ThemeProvider programmatically

import { ThemeProvider } from 'react-18-ui-library'

<ThemeProvider tokens={{ colorPrimary: '#7c3aed', radiusMd: '0.5rem' }}>
  <App />
</ThemeProvider>

Component Reference

Layout

| Component | Description | |---|---| | AppShell | Full-page layout combining Navbar + Sidebar + main content area | | Navbar | Top navigation bar with logo, links, and action slots | | Sidebar | Collapsible sidebar with icons, badges, and nested items | | Container | Max-width content wrapper with xs/sm/md/lg/xl/full sizes | | Stack | Flexbox stack — direction, gap, align, justify props | | Grid | CSS grid with responsive column count and gap control | | Divider | Horizontal or vertical separator with optional label | | Spacer | Flexible whitespace filler |

import { AppShell } from 'react-18-ui-library'
import { LayoutDashboard, Users, Settings } from 'lucide-react'

<AppShell
  navbar={{
    logo: <img src="/logo.svg" alt="Logo" height={32} />,
    links: [{ label: 'Home', href: '/', active: true }],
    actions: <Button size="sm">Sign Out</Button>,
  }}
  sidebar={{
    items: [
      { id: 'dash',     label: 'Dashboard', icon: <LayoutDashboard size={16} />, active: true },
      { id: 'users',    label: 'Users',     icon: <Users size={16} />, badge: 5 },
      { id: 'settings', label: 'Settings',  icon: <Settings size={16} /> },
    ],
  }}
>
  <main>Page content</main>
</AppShell>

Navigation

| Component | Description | |---|---| | Tabs | Horizontal/vertical tab panels with line, pill, boxed variants | | Breadcrumb | Accessible breadcrumb trail with custom separator | | Pagination | Page navigation with configurable page size and sibling count | | StepIndicator | Multi-step wizard progress with completed/current/upcoming states |

import { Tabs, StepIndicator, Pagination } from 'react-18-ui-library'

<Tabs
  items={[
    { id: 'profile', label: 'Profile', content: <ProfilePanel /> },
    { id: 'billing', label: 'Billing', content: <BillingPanel /> },
    { id: 'security', label: 'Security', content: <SecurityPanel /> },
  ]}
  variant="pill"
/>

<StepIndicator
  steps={[
    { id: '1', label: 'Account', status: 'completed' },
    { id: '2', label: 'Profile', status: 'current' },
    { id: '3', label: 'Review',  status: 'upcoming' },
  ]}
/>

<Pagination
  page={currentPage}
  totalPages={20}
  onPageChange={setCurrentPage}
/>

Inputs

| Component | Description | |---|---| | Button | primary, secondary, ghost, outline, destructive variants + xs/sm/md/lg sizes | | IconButton | Square icon-only button with same variants | | TextField | Text input — prefix/suffix icon/text/image, clearable, password toggle, showMaxLength | | TextArea | Auto-resize textarea with character count (showCharCount / showMaxLength) | | Select | Radix-powered dropdown with option groups | | MultiSelect | Multi-value select with chip display | | SearchSelect | Combobox with single/multi mode and async search | | ChipSelect | Inline chip-toggle multi-select | | Checkbox | Accessible checkbox with indeterminate state | | RadioGroup | Accessible radio group | | Switch | Toggle switch | | Slider | Range slider with min/max/step | | NumberInput | Numeric input — increment/decrement controls, hideControls, blocks non-numeric keys, showMaxLength | | PhoneInput | Phone input with country code dropdown (30+ countries), digit-only enforcement, showMaxLength | | OTPInput | One-time password input with configurable digit count and auto-advance | | Rating | Star rating with half-star support | | TagInput | Free-form tag entry — Enter/comma separators, duplicate prevention | | DatePicker | Calendar picker — single/range mode, month+year dropdowns, configurable year range, portal rendering |

import { TextField, PhoneInput, NumberInput, DatePicker, OTPInput } from 'react-18-ui-library'
import { Search } from 'lucide-react'

// Text field with icon prefix and max-length counter
<TextField
  label="Username"
  prefixIcon={<Search size={14} />}
  maxLength={30}
  showMaxLength
  clearable
/>

// Phone input with country code
<PhoneInput
  label="Mobile Number"
  defaultCountry="IN"
  maxLength={10}
  showMaxLength
  onChange={(val) => console.log(val.full)} // "+91 9876543210"
/>

// Number input — hide stepper buttons
<NumberInput
  label="Quantity"
  hideControls
  min={0}
  max={999}
/>

// Date picker — restrict year range
<DatePicker
  label="Date of Birth"
  yearRangeBefore={80}
  yearRangeAfter={0}
  onChange={(date) => console.log(date)}
/>

// OTP input — 6 digits
<OTPInput length={6} onComplete={(code) => verifyOTP(code)} />

Forms

JSONForm — Schema-Driven Forms

Build complete, validated forms from a JSON schema array. Powered by react-hook-form.

Supported type values: text · email · password · url · tel · number · textarea · select · multiselect · radio · checkbox · switch · slider · chipselect · taginput · datepicker · file

import { JSONForm, v } from 'react-18-ui-library'
import type { JSONFormField } from 'react-18-ui-library'

const schema: JSONFormField[] = [
  {
    name: 'firstName',
    type: 'text',
    label: 'First Name',
    colSpan: 1,
    validator: v.string().required().lengthBetween(2, 50),
  },
  {
    name: 'email',
    type: 'email',
    label: 'Email',
    colSpan: 1,
    validator: v.email().required().blockedDomains(['mailinator.com']),
  },
  {
    name: 'role',
    type: 'select',
    label: 'Role',
    options: [
      { value: 'admin',  label: 'Admin' },
      { value: 'editor', label: 'Editor' },
      { value: 'viewer', label: 'Viewer' },
    ],
    validator: v.select().required(),
  },
  {
    name: 'bio',
    type: 'textarea',
    label: 'Bio',
    colSpan: 2,
    showCharCount: true,
    validator: v.string().maxLength(300),
  },
  {
    name: 'terms',
    type: 'checkbox',
    label: 'I agree to the Terms and Conditions',
    colSpan: 2,
    validator: v.boolean().mustBeTrue('You must accept the terms'),
  },
]

<JSONForm
  schema={schema}
  columns={2}
  onSubmit={(data) => console.log(data)}
  onCancel={() => router.back()}
  submitLabel="Create Account"
  cancelLabel="Cancel"
/>

FileUpload — Drag & Drop

import { FileUpload } from 'react-18-ui-library'

<FileUpload
  label="Upload Documents"
  accept={{ 'image/*': ['.png', '.jpg'], 'application/pdf': ['.pdf'] }}
  maxSize={5 * 1024 * 1024}
  maxFiles={10}
  files={files}
  onFilesChange={setFiles}
  onFileRemove={(id) => setFiles(prev => prev.filter(f => f.id !== id))}
/>

FormValidator — Fluent Validation API

A chainable, type-safe validation builder that compiles to react-hook-form rules. Import v and attach a .validator to any JSONFormField.

Validator reference

| Builder | Field types | Methods | |---|---|---| | v.string() | text textarea | .required() .minLength(n) .maxLength(n) .lengthBetween(min,max) .length(n) .shouldMatch(regex) .shouldBeIn([...]) .notEmpty() .noSpaces() .alphanumeric() .startsWith(s) .endsWith(s) .custom(name, fn) | | v.number() | number | .required() .min(n) .max(n) .between(min,max) .integer() .positive() .negative() .nonZero() .multipleOf(n) .length(digits) .custom(name, fn) | | v.password() | password | .required() .minLength(n) .maxLength(n) .hasUppercase() .hasLowercase() .hasDigit() .hasSpecialChar() .noSpaces() .confirmMatch(getter) .strong() .custom(name, fn) | | v.email() | email | .required() .domain([...]) .blockedDomains([...]) .shouldMatch(regex) .custom(name, fn) | | v.url() | url | .required() .httpsOnly() .allowedDomains([...]) .noTrailingSlash() .custom(name, fn) | | v.tel() | tel | .required() .length(n) .minLength(n) .maxLength(n) .shouldMatch(regex) .custom(name, fn) | | v.date() | datepicker text | .required() .validDate() .minDate(d) .maxDate(d) .notInPast() .notInFuture() .between(min,max) .custom(name, fn) | | v.select() | select radio | .required() .shouldBeIn([...]) .notEmpty() .custom(name, fn) | | v.boolean() | checkbox switch | .required() .mustBeTrue(msg) .custom(name, fn) | | v.array() | chipselect taginput multiselect | .required() .minItems(n) .maxItems(n) .exactItems(n) .shouldBeIn([...]) .noEmpty() .custom(name, fn) | | v.file() | file | .required() .maxSize(bytes) .allowedTypes([...]) .allowedExtensions([...]) .maxFiles(n) .minFiles(n) .custom(name, fn) |

Usage examples

import { v } from 'react-18-ui-library'

// String
v.string().required().lengthBetween(3, 20).noSpaces()
v.string().required().shouldBeIn(['admin', 'editor', 'viewer'])
v.string().required().shouldMatch(/^\d{5}(-\d{4})?$/, 'Invalid ZIP code')
v.string().required().startsWith('INVITE-').alphanumeric()

// Number
v.number().required().between(18, 120).integer()
v.number().required().positive().nonZero().multipleOf(5)
v.number().required().length(6)  // exactly 6 digits (PIN)

// Password with confirm-match
const passwordRef = useRef('')
// field 1
v.password().required().strong()
// field 2 — reads field 1's value via getter
v.password().required().confirmMatch(() => passwordRef.current)

// Email
v.email().required()
v.email().required().domain(['company.com', 'company.org'])       // whitelist
v.email().required().blockedDomains(['mailinator.com', 'yopmail.com']) // blacklist

// URL
v.url().required().httpsOnly().noTrailingSlash()
v.url().required().allowedDomains(['github.com'])

// Phone
v.tel().required().length(10)
v.tel().required().minLength(7).maxLength(15)

// Date
v.date().required().notInPast()
v.date().required().notInFuture()
v.date().required().between(new Date('2020-01-01'), new Date('2030-12-31'))

// Select / Radio
v.select().required().shouldBeIn(['us', 'gb', 'in', 'au'])

// Boolean
v.boolean().mustBeTrue('You must accept the terms')

// Array (chipselect / taginput)
v.array().required().minItems(2).maxItems(5)
v.array().required().exactItems(3).noEmpty()

// File upload
v.file().required()
  .maxSize(5 * 1024 * 1024)
  .allowedTypes(['image/*', 'application/pdf'])
  .maxFiles(3)

// Custom rule
v.string().required().custom(
  'noBadWords',
  (val) => !String(val).includes('spam'),
  'Inappropriate content detected'
)

Display

| Component | Description | |---|---| | Card | Surface container with optional header, footer, and padding variants | | Box | Generic styled div with spacing and color props | | Image | Responsive image with aspect ratio (square/video/portrait) and fit control | | Avatar / AvatarGroup | User avatar with fallback initials, status dot, and group stacking | | Badge / BadgeAnchor | Status badge chip and notification dot anchor | | Tag | Dismissible label chip with color variants | | Icon | Lucide icon wrapper with xs/sm/md/lg/xl sizes | | SVG | Raw SVG wrapper with size props | | Table | Sortable, selectable data table with sticky header | | DataTable | Full-featured table with server-side pagination and column visibility | | List / ListItem | Semantic list with icon, description, and action slots | | Timeline | Vertical event timeline with icon and color variants | | Stat | Metric card with label, value, delta, and trend indicator | | EmptyState | Zero-state placeholder with icon, title, description, and CTA | | MarkdownReader | Renders Markdown with GFM tables and syntax-highlighted code blocks | | Collapsible | Animated expand/collapse panel | | TreeView | Recursive tree with expand/collapse nodes and selection |


Feedback

| Component | Description | |---|---| | Alert | Inline status message — info / success / warning / error | | ToastProvider + useToast | Global toast notification system with position control | | Spinner | Loading spinner — xs through xl sizes | | Skeleton / SkeletonCard | Content placeholder shimmer animations | | ProgressBar | Linear progress with default / success / warning / error variants | | ErrorBoundary | React error boundary with customizable fallback UI | | FullScreenLoader + useFullScreenLoader | Full-viewport loading overlay with programmatic control |

import { ToastProvider, useToast } from 'react-18-ui-library'

// Wrap your app root
<ToastProvider toasts={toasts} onDismiss={dismiss} position="bottom-right" />

// In any component
const { toast, dismiss } = useToast()
toast({ title: 'Saved!',  description: 'Changes saved.',       variant: 'success' })
toast({ title: 'Error',   description: 'Something went wrong.', variant: 'error' })
toast({ title: 'Warning', description: 'Low disk space.',       variant: 'warning' })

Overlay

| Component | Description | |---|---| | Modal | Radix Dialog modal — sm/md/lg/xl/full sizes | | Drawer | Slide-in panel from top/right/bottom/left | | Tooltip | Hover tooltip with top/right/bottom/left placement | | Popover | Click-triggered floating panel | | ContextMenu | Right-click context menu with icons and dividers | | ConfirmDialog + useConfirm | Programmatic async confirmation dialog | | CommandPalette + useCommandPalette | ⌘K-style command palette with groups and keyboard navigation, portal-rendered |

import { useConfirm, ConfirmDialogProvider } from 'react-18-ui-library'

// Wrap app root
<ConfirmDialogProvider>
  <App />
</ConfirmDialogProvider>

// In any component
const confirm = useConfirm()
const ok = await confirm({
  title: 'Delete record?',
  description: 'This action cannot be undone.',
  confirmLabel: 'Delete',
  variant: 'destructive',
})
if (ok) deleteRecord()
import { CommandPalette, useCommandPalette } from 'react-18-ui-library'

const { open, close, isOpen } = useCommandPalette()

// Bind ⌘K
useKeyboard([{ key: 'k', meta: true, handler: open }])

<CommandPalette
  open={isOpen}
  onClose={close}
  groups={[
    {
      id: 'nav',
      label: 'Navigation',
      items: [
        { id: 'dashboard', label: 'Dashboard', icon: <LayoutDashboard size={14} />, onSelect: () => router.push('/') },
        { id: 'settings',  label: 'Settings',  icon: <Settings size={14} />,        onSelect: () => router.push('/settings') },
      ],
    },
  ]}
/>

Typography

| Component | Description | |---|---| | Heading | h1h6 with independent size (xs4xl) and weight props | | Text | Paragraph / span with size, weight, color, and polymorphic as prop | | Label | Accessible form label | | Code | Inline code or syntax-highlighted fenced code block (Prism) | | Link | Anchor with none/hover/always underline variants | | Kbd | Keyboard shortcut key display |

import { Heading, Text, Code, Kbd, Link } from 'react-18-ui-library'

<Heading level="h1" size="3xl" weight="bold">Welcome back</Heading>
<Text color="muted" size="sm">Last updated 2 minutes ago</Text>
<Text>
  Press <Kbd>⌘</Kbd> + <Kbd>K</Kbd> to open the command palette.
</Text>
<Code block language="tsx">{`const x: number = 42`}</Code>
<Link href="/docs" underline="hover">Read the docs</Link>

Actions

| Component | Description | |---|---| | CopyButton | One-click copy to clipboard with animated feedback state |


Hooks

| Hook | Signature | Description | |---|---|---| | useToast | () => { toast, dismiss, toasts } | Trigger and dismiss toast notifications | | useTheme | () => { tokens, setToken } | Read and update CSS token values at runtime | | useMediaQuery | (query: string) => boolean | Reactive media query boolean | | useClickOutside | (ref, handler) | Fire handler when clicking outside a ref | | useDebounce | (value, delay) => value | Debounce any value | | useLocalStorage | (key, initial) => [value, setter] | Persistent state backed by localStorage | | useClipboard | () => { copy, copied } | Copy text with copied feedback state | | useWindowSize | () => { width, height } | Reactive window dimensions | | useIntersectionObserver | (ref, options) => entry | Intersection Observer hook | | useKeyboard | (shortcuts[]) => void | Bind keyboard shortcuts declaratively | | useConfirm | () => confirmFn | Programmatic confirm dialog (needs ConfirmDialogProvider) | | useCommandPalette | () => { open, close, isOpen } | Control the command palette | | useFullScreenLoader | () => { show, hide, isVisible } | Show/hide the full-screen loading overlay |

import { useDebounce, useLocalStorage, useKeyboard, useClipboard } from 'react-18-ui-library'

const debouncedQuery = useDebounce(searchQuery, 300)
const [theme, setTheme] = useLocalStorage('theme', 'light')
const { copy, copied } = useClipboard()

useKeyboard([
  { key: 'k', meta: true, handler: () => openPalette() },
  { key: 'Escape',        handler: () => closeModal() },
])

CSS Token Reference

| Token | Default | Purpose | |---|---|---| | --color-primary | #6366f1 | Primary brand color | | --color-primary-hover | #4f46e5 | Primary hover state | | --color-primary-foreground | #ffffff | Text on primary background | | --color-background | #f8fafc | Page background | | --color-surface | #ffffff | Card / panel background | | --color-text | #0f172a | Primary text | | --color-text-muted | #64748b | Secondary / helper text | | --color-border | #e2e8f0 | Default border color | | --color-error | #ef4444 | Error / destructive state | | --color-success | #22c55e | Success / positive state | | --color-warning | #f59e0b | Warning / caution state | | --radius-sm | 0.25rem | Small border radius | | --radius-md | 0.375rem | Default border radius | | --radius-lg | 0.5rem | Large border radius | | --font-family-base | system-ui | Base font family | | --navbar-height | 64px | Navbar height | | --sidebar-width | 260px | Sidebar expanded width | | --sidebar-collapsed-width | 64px | Sidebar collapsed width |


Security — Snyk

This library uses Snyk for dependency vulnerability scanning. Snyk runs automatically as a prebuild hook — the build is blocked if any high or critical severity vulnerabilities are found.

Setup (one-time)

# Authenticate with Snyk (opens browser)
npx snyk auth

Available security scripts

| Script | Description | |---|---| | npm run snyk:test | Scan for high + critical vulnerabilities (used in prebuild) | | npm run snyk:test:all | Scan for all severities including low and medium | | npm run snyk:monitor | Upload snapshot to Snyk dashboard for continuous monitoring | | npm run snyk:report | Run scan and generate reports/snyk-report.html | | npm run snyk:report:html | Save raw JSON results then generate HTML report |

How the build gate works

npm run build
  └─ prebuild → snyk test --severity-threshold=high
       ├─ PASS → build proceeds normally
       └─ FAIL → build aborted (exit code 1)

The .snyk policy file at the project root can be used to suppress known/accepted vulnerabilities:

# .snyk
ignore:
  SNYK-JS-EXAMPLE-12345:
    - '*':
        reason: Not exploitable in this context
        expires: 2026-12-31T00:00:00.000Z

Development

# Install dependencies
npm install

# Authenticate Snyk (first time only)
npx snyk auth

# Start Storybook dev server (port 6006)
npm run dev

# Type check
npm run type-check

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

# Lint
npm run lint

# Build library (runs snyk:test first via prebuild hook)
npm run build

# Build Storybook static site
npm run build-storybook

# Run Snyk vulnerability scan
npm run snyk:test

# Generate HTML vulnerability report
npm run snyk:report:html
# → opens reports/snyk-report.html

Peer Dependencies

| Package | Version | |---|---| | react | >= 18.0.0 | | react-dom | >= 18.0.0 |

TailwindCSS is not a peer dependency — the library ships pre-compiled CSS. You do not need Tailwind in your consuming app.


License

MIT © react-18-ui-library contributors