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

@underverse-ui/underverse

v2.0.23

Published

Underverse UI – reusable React/Next.js UI components

Downloads

9,200

Readme

Highlights

  • More than 60 typed components, from basic controls to DataTable, CalendarTimeline, and UEditor.
  • React 18+ support with ESM and CommonJS package outputs.
  • Tailwind CSS 4 design tokens, dark-mode support, and global component configuration.
  • Built-in English, Vietnamese, Korean, and Japanese UI messages.
  • Keyboard and ARIA behavior covered by component interaction tests.
  • Public API metadata and bundle budgets verified before publishing.

Installation

npm install @underverse-ui/underverse

The package declares its framework and feature integrations as peer dependencies. Use React 18 or newer and Tailwind CSS 4 in the consuming application.

Quick start

"use client";

import {
  Button,
  DatePicker,
  TranslationProvider,
  UnderverseConfigProvider,
} from "@underverse-ui/underverse";

export function Example() {
  return (
    <TranslationProvider locale="en">
      <UnderverseConfigProvider config={{ borderMode: "lg" }}>
        <div className="flex items-center gap-3">
          <DatePicker onChange={(date) => console.log(date)} />
          <Button>Continue</Button>
        </div>
      </UnderverseConfigProvider>
    </TranslationProvider>
  );
}

TranslationProvider is optional. Components fall back to English when it is not mounted.

Tailwind CSS setup

Tailwind ignores dependencies in node_modules during automatic source detection. Register Underverse from the stylesheet that imports Tailwind:

@import "tailwindcss";
@source "../node_modules/@underverse-ui/underverse/dist";

The source path is relative to that stylesheet, so adjust ../ when the file lives under src/app or another nested directory. See Tailwind's explicit source registration documentation.

Underverse uses semantic Tailwind tokens. Applications that already use shadcn-style tokens can reuse them. A minimal starter map looks like this:

:root {
  --background: oklch(0.98 0.005 255);
  --foreground: oklch(0.22 0.012 255);
  --card: oklch(1 0 0);
  --card-foreground: var(--foreground);
  --popover: oklch(1 0 0);
  --popover-foreground: var(--foreground);
  --primary: oklch(0.55 0.18 255);
  --primary-foreground: oklch(0.985 0 0);
  --secondary: oklch(0.58 0.12 280);
  --secondary-foreground: oklch(0.985 0 0);
  --muted: oklch(0.955 0.01 255);
  --muted-foreground: oklch(0.4 0.012 255);
  --accent: color-mix(in oklch, var(--primary) 16%, var(--background));
  --accent-foreground: var(--foreground);
  --destructive: oklch(0.56 0.19 22);
  --destructive-foreground: oklch(0.985 0 0);
  --success: oklch(0.52 0.16 145);
  --success-foreground: oklch(0.985 0 0);
  --warning: oklch(0.62 0.16 60);
  --warning-foreground: oklch(0.16 0.012 255);
  --info: oklch(0.62 0.14 210);
  --info-foreground: oklch(0.985 0 0);
  --border: oklch(0.7 0.01 255 / 22%);
  --input: oklch(0.7 0.01 255 / 30%);
  --ring: color-mix(in oklch, var(--primary) 70%, var(--background));
  --primary-soft: color-mix(in oklch, var(--primary) 12%, var(--background));
  --secondary-soft: color-mix(in oklch, var(--secondary) 12%, var(--background));
  --destructive-soft: color-mix(in oklch, var(--destructive) 12%, var(--background));
  --success-soft: color-mix(in oklch, var(--success) 12%, var(--background));
  --warning-soft: color-mix(in oklch, var(--warning) 14%, var(--background));
  --info-soft: color-mix(in oklch, var(--info) 12%, var(--background));
  --input-focus: var(--ring);
  --input-disabled: var(--muted);
  --input-invalid: var(--destructive);
  --surface-0: var(--background);
  --surface-1: oklch(0.99 0.006 255);
  --surface-2: oklch(0.98 0.007 255);
  --surface-3: oklch(0.97 0.008 255);
  --shadow-xs: 0 1px 2px rgb(0 0 0 / 5%);
  --shadow-sm: 0 1px 3px rgb(0 0 0 / 10%);
  --shadow-md: 0 4px 6px rgb(0 0 0 / 10%);
  --shadow-lg: 0 10px 15px rgb(0 0 0 / 10%);
  --shadow-xl: 0 20px 25px rgb(0 0 0 / 12%);
}

@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-card: var(--card);
  --color-card-foreground: var(--card-foreground);
  --color-popover: var(--popover);
  --color-popover-foreground: var(--popover-foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
  --color-primary-soft: var(--primary-soft);
  --color-secondary: var(--secondary);
  --color-secondary-foreground: var(--secondary-foreground);
  --color-secondary-soft: var(--secondary-soft);
  --color-muted: var(--muted);
  --color-muted-foreground: var(--muted-foreground);
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
  --color-destructive: var(--destructive);
  --color-destructive-foreground: var(--destructive-foreground);
  --color-destructive-soft: var(--destructive-soft);
  --color-success: var(--success);
  --color-success-foreground: var(--success-foreground);
  --color-success-soft: var(--success-soft);
  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);
  --color-warning-soft: var(--warning-soft);
  --color-info: var(--info);
  --color-info-foreground: var(--info-foreground);
  --color-info-soft: var(--info-soft);
  --color-border: var(--border);
  --color-input: var(--input);
  --color-ring: var(--ring);
  --color-input-focus: var(--input-focus);
  --color-input-disabled: var(--input-disabled);
  --color-input-invalid: var(--input-invalid);
  --color-surface-0: var(--surface-0);
  --color-surface-1: var(--surface-1);
  --color-surface-2: var(--surface-2);
  --color-surface-3: var(--surface-3);
  --shadow-xs: var(--shadow-xs);
  --shadow-sm: var(--shadow-sm);
  --shadow-md: var(--shadow-md);
  --shadow-lg: var(--shadow-lg);
  --shadow-xl: var(--shadow-xl);
  --ease-soft: cubic-bezier(0.25, 1, 0.5, 1);
}

For the complete light/dark palette, see the Underverse color system.

Imports and bundle size

The root entry is the standard public API:

import { Button, DataTable, UEditor } from "@underverse-ui/underverse";

It is tree-shakeable and protected by a package bundle budget. Existing applications do not need to change their imports when upgrading.

For an application that only uses the editor, the explicit UEditor entry creates a smaller, clearer module boundary:

import UEditor, { type UEditorRef } from "@underverse-ui/underverse/ueditor";

Do not deep-import files under dist or src; only the root and /ueditor entries are public contracts.

UEditor

UEditor is a Tiptap-based rich-text editor with slash commands, menus, uploads, tables, formulas, cell formatting, resizing, and spreadsheet paste support.

"use client";

import { useRef, useState } from "react";
import UEditor, { type UEditorRef } from "@underverse-ui/underverse/ueditor";

export function ArticleEditor() {
  const editorRef = useRef<UEditorRef>(null);
  const [html, setHtml] = useState("<p>Start writing…</p>");

  return (
    <UEditor
      ref={editorRef}
      content={html}
      onHtmlChange={setHtml}
      outputDebounceMs={120}
      placeholder="Type '/' for commands…"
      showCharacterCount
    />
  );
}

Use outputDebounceMs for large controlled documents. Before persisting base64 images, call editorRef.current?.prepareContentForSave() with an upload handler.

Read the UEditor guide · Read the table guide

Internationalization

Standalone React applications can use the built-in provider:

import { TranslationProvider } from "@underverse-ui/underverse";

<TranslationProvider locale="vi">{children}</TranslationProvider>;

Supported locales:

| Code | Language | | --- | --- | | en | English | | vi | Tiếng Việt | | ko | 한국어 | | ja | 日本語 |

For a next-intl application, mount NextIntlAdapter inside NextIntlClientProvider. The adapter reads the active locale and messages, then falls back to the package's built-in locale messages for missing component strings.

Optional integrations

Install and configure only the integrations used by your application:

| Feature | Integration | | --- | --- | | Forms and schema validation | react-hook-form, @hookform/resolvers, zod | | UEditor | Tiptap, Lowlight, and Tippy peers declared by the package | | Overlay scrollbars | overlayscrollbars and its stylesheet | | Next.js image/i18n adapters | next, next-intl |

Overlay scrollbars are opt-in and component-scoped:

import "overlayscrollbars/overlayscrollbars.css";
import {
  OverlayScrollbarProvider,
  ScrollArea,
} from "@underverse-ui/underverse";

<OverlayScrollbarProvider autoHide="leave">
  <ScrollArea className="h-64" useOverlayScrollbar>
    {content}
  </ScrollArea>
</OverlayScrollbarProvider>;

When a Combobox is rendered inside a secondary browser window, point its portal at that window's document. Existing usages can omit this prop and keep using the current document body.

const popupContainerRef = useRef<HTMLDivElement>(null);

<Combobox
  usePortal
  getPortalContainer={() =>
    popupContainerRef.current?.ownerDocument.body ?? null
  }
  value={value}
  options={options}
  onChange={setValue}
/>;

Component families

| Area | Components | | --- | --- | | Inputs | Input, Textarea, CheckBox, RadioGroup, Switch, Slider, TagInput | | Selection | Combobox, MultiCombobox, CategoryTreeSelect, ColorPicker | | Date and time | Calendar, DatePicker, DateRangePicker, DateTimePicker, TimePicker, MonthYearPicker | | Data | Table, DataTable, Pagination, Grid, List, Timeline | | Navigation | Tabs, Breadcrumb, DropdownMenu, Section, ScrollArea | | Overlays | Modal, Sheet, Popover, Tooltip, Toast, Alert | | Media and content | SmartImage, ImageUpload, FileUpload, Carousel, EmojiPicker, StickerPicker, UEditor | | Scheduling | CalendarTimeline |

Browse every component, example, and generated API contract in the live documentation.

Compatibility

| Runtime | Support | | --- | --- | | React | 18 and newer | | Node.js | 18 and newer | | Next.js | 13 and newer | | Tailwind CSS | 4.x | | Module formats | ESM and CommonJS | | TypeScript | Type declarations included |

Package metadata

The npm package also ships:

  • api-reference.json for a generated machine-readable export index.
  • llms.txt and agent-recipes.json for coding assistants.
  • AGENTS.md for package integration rules.

Support

License

MIT © Tran Van Bach