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

@djangocfg/ui-core

v2.1.466

Published

Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps

Readme

@djangocfg/ui-core

@djangocfg/ui-core

Framework-agnostic React UI library: 70+ shadcn/Radix components on Tailwind v4, semantic theme tokens, palette hooks for Canvas/SVG, plus a router-adapter system that lets the same <Link> / <Sidebar> / <SSRPagination> work under Next.js, Vite, Electron, Wails, or plain React.

Part of DjangoCFG. Live demo.

Install

pnpm add @djangocfg/ui-core

Works in any React host. Next.js apps import components from here directly and add the Next router adapter from @djangocfg/ui-core/adapters/nextjs (see Router adapters below); Next-specific server utilities (sitemap, health, OG images, config) live in the separate @djangocfg/nextjs package.

Import styles once at the app root — use the golden path, which pins everything to the right cascade layers so import order can't break layout utilities:

/* FIRST style import in your app entry CSS */
@import "@djangocfg/ui-core/styles/full";   /* Tailwind v4 + tokens + base + utilities, layer-safe */

The plain @djangocfg/ui-core/styles entry does NOT import Tailwind and emits its CSS unlayered — if you use it you own the layer ordering (import tailwindcss first). See src/styles/README.md § App setup for why. Prefer …/styles/full.

Quick start

import { UiProviders, Button, Card } from '@djangocfg/ui-core';

<UiProviders>
  <Card><Button>Hello</Button></Card>
</UiProviders>

<UiProviders> mounts Tooltip / Dialog / Toast in the right order and a top-level error Boundary (a crash shows a recoverable fallback, not a white screen). Mount it once at the root — library components (and everything in @djangocfg/ui-tools) trust it to be there and never nest their own (a second TooltipProvider is the canonical "Tooltip must be used within TooltipProvider" trap). Pass onError to forward crashes to your logger, errorFallback for a custom (e.g. i18n) crash screen, or errorBoundary={false} to opt out.

Catalogue

| Group | Examples | |---|---| | components/data/ | Avatar · Badge · Card · Table · BalancedText · Skeleton | | components/forms/ | Button · Input · Textarea · Select · Switch · Checkbox · Slider · Form | | components/feedback/ | Alert · Toast · Banner · Progress · Spinner | | components/overlay/ | Dialog · Drawer · Popover · Tooltip · HoverCard · Sheet · ContextMenu · DropdownMenu | | components/navigation/ | Sidebar · Tabs · Breadcrumb · Pagination · NavigationMenu · Command | | components/layout/ | Container · Grid · Stack · Separator · ScrollArea · Sticky | | components/select/ | Combobox · MultiSelect | | components/effects/ | Glass · Marquee · Backdrop | | components/specialized/ | Accordion · Collapsible · Toggle · Calendar · DatePicker | | components/boundary/ | ErrorBoundary |

Imports stay flat — group folders are organisational.

Hooks (/hooks)

| Topic | Hooks | |---|---| | dom/ | useSize · useResizeObserver · useMeasure · useMutationObserver · useIntersection | | device/ | useIsMobile · useIsTouch · useMediaQuery · useOnline · useViewportSize · useOrientation | | state/ | useLocalStorage · useSessionStorage · useToggle · useCounter · useDebouncedValue | | events/ | useEventListener · useClickOutside · useKeyPress · useFocusWithin | | theme/ | useTheme · useResolvedTheme · useThemePreset | | feedback/ | useToast · useDialog · useClipboard · useConfirm | | hotkey/ | useHotkey (single key + chord) | | audio/ | useBeep · useSpeak (Web Speech) | | tabs/ | useCrossTab (BroadcastChannel coordination) | | media/ | useMediaPermissions · useUserMedia · useDevices | | time/ | useNow · useInterval · useTimeout | | router/ | useLink · useRouter (router-adapter consumer) |

Lib utilities (/lib)

  • cn(...)clsx + tailwind-merge shortcut
  • getIntensity(value, thresholds) — quantise values into discrete bins (heatmaps, gauges)
  • createLogger() — leveled console logger
  • dialog-service — imperative confirm() / alert() / prompt() returning promises
  • persist — typed localStorage / sessionStorage hooks
  • pretext (subpath @djangocfg/ui-core/lib/pretext) — DOM-free text measurement via @chenglou/pretext; powers <BalancedText> and is the primitive for non-CSS line balancing

Router adapters

The router-aware primitives (Sidebar, Link, SSRPagination, and the useRouter / useNavigate / useLocation hooks) navigate through a pluggable adapter, so the same components work under any host. ui-core itself is router-agnostic and has zero router dependency — pick the adapter for your host and mount it once near the root.

Which adapter for which host

| Host | Mount this | Extra dependency you install | |---|---|---| | Next.js (App Router) | NextRouterAdapter from @djangocfg/ui-core/adapters/nextjs | next (you already have it) | | Vite / React-Router SPA | ReactRouterProvider from @djangocfg/ui-core/adapters/react-router | react-router ≥7 — add to your app's dependencies | | Wails / Electron / plain React / Storybook | nothing — the default History-API adapter is used automatically | none (zero deps) |

next and react-router are optional peers: the base package never imports them. Only the matching sub-path entry does, so the dep is resolved only when you import that adapter. Never add both — import the one sub-path for your host.

Where to mount it

Both provider adapters wrap a subtree; every ui-core router call inside it flows through your app's router. Mount once, near the root:

// Next.js — inside the client provider stack (below your i18n provider)
import { NextRouterAdapter } from '@djangocfg/ui-core/adapters/nextjs';
<NextRouterAdapter><AppLayout>{children}</AppLayout></NextRouterAdapter>

// Vite / React-Router — INSIDE the router context (an element RouterProvider
// renders, e.g. a layout route wrapping <Outlet/>), NOT around <RouterProvider>.
import { ReactRouterProvider } from '@djangocfg/ui-core/adapters/react-router';
function Shell() {
  return <ReactRouterProvider><Layout><Outlet /></Layout></ReactRouterProvider>;
}

React-Router gotcha: mounting ReactRouterProvider around <RouterProvider> throws — that subtree has no router context, so useNavigate fails. Always mount it inside.

What happens if you skip it

Without an adapter, hooks fall back to the default History-API adapter (window.history.pushState + window.location). This is intentional and works everywhere with zero deps — but in a framework host it silently loses the framework's routing behavior:

  • Next.js without NextRouterAdapter: no RSC refetch on navigation, no route loader, no prefetch. Links still change the URL, but the App Router doesn't react. → always mount it in Next apps.
  • React-Router without ReactRouterProvider: programmatic useNavigate / <Link> mutate window.history directly and bypass the <Outlet/> swap, so navigation desyncs from the data router. → always mount it in RR SPAs.

For a plain SPA (Wails/Electron/CRA) the History-API default is the right backend — mount nothing.

Link adapters (optional, Next.js)

By default <Link> from @djangocfg/ui-core/components renders a plain <a>. To route Next's prefetch / RSC handling through it, also mount NextLinkProvider alongside NextRouterAdapter. For locale-prefixed hrefs with next-intl, pass createNextIntlLinkAdapter(IntlLink) as AppLayout's linkAdapter — both live in @djangocfg/ui-core/adapters/nextjs.

Deeper dive: full adapter reference, the useRouter / useNavigate / useLocation / useQueryState hook surface, and a ~20-line recipe for custom routers (TanStack Router, wouter, Remix, custom transports) live in src/hooks/router/README.md.

Using @djangocfg/layouts? BaseApp already mounts the Next adapters for you — you don't wire them by hand.

Theming (/styles)

Tailwind v4 with semantic tokens, not raw color scales:

<Card className="bg-card border-border text-foreground" />
<Button className="bg-primary text-primary-foreground" />
<Alert className="bg-warning-background text-warning-foreground border-warning-border" />

Tokens live in :root / .dark as fully-wrapped CSS colors; @theme inline exposes them as --color-X references, so opacity modifiers (bg-card/40, border-foreground/20) resolve via color-mix for every semantic token.

Preset text/fill pairs target WCAG AA for compact product UI. For the macOS preset this is enforced by pnpm check:contrast; primary and destructive foregrounds intentionally switch between light and dark ink where the system fill's luminance requires it.

@custom-variant dark (&:where(.dark, .dark *)) binds the dark: variant to the .dark class on <html> (not prefers-color-scheme) — every theme-switcher in this monorepo toggles that class.

Tailwind's text-* size utilities are bridged to the preset --font-size-* scale. Product-specific density belongs in a small CSS override after the static preset import — never in a runtime token builder. See src/styles/README.md.

Programmatic theme colors for Canvas / SVG / Mermaid:

import { useThemeColor, alpha, useStylePresets } from '@djangocfg/ui-core/styles/palette';

const primary = useThemeColor('primary');           // #00d9ff (hex, not oklch)
const dim = alpha(primary, 0.15);                   // 'rgba(0, 217, 255, 0.15)'
const { success, warning, danger } = useStylePresets();

Always hex-strings — color-mix(...) / oklch(...) syntax is rejected by Canvas2D fillStyle.

Live playground covers all tokens, presets, and dark-mode pairs.

Maintenance rule

After any change to components or hooks — update this README and bump the package patch version. Consumers pin to npm versions; surface drift in this file is the canonical changelog.

Requirements

  • React 18 or 19
  • Tailwind CSS v4 (host imports @djangocfg/ui-core/styles)

License

MIT — © djangocfg.