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

@voyantjs/admin

v0.80.18

Published

Reusable admin dashboard primitives for Voyant templates. Pure, transport-agnostic React providers and helpers — no UI components tied to a specific shadcn copy.

Readme

@voyantjs/admin

Reusable admin dashboard primitives for Voyant templates. Pure, transport-agnostic React providers and helpers — no UI components tied to a specific shadcn copy.

Install

pnpm add @voyantjs/admin

Usage

import { AdminProvider } from "@voyantjs/admin/providers/admin-provider"
import { OperatorAdminShellProvider } from "@voyantjs/admin/providers/operator-admin-shell"
import { ThemeProvider, useTheme } from "@voyantjs/admin/providers/theme"
import { makeQueryClient } from "@voyantjs/admin/providers/query-client"
import { getInitials, getDisplayName } from "@voyantjs/admin/lib/initials"
import { OperatorAdminWorkspaceLayout } from "@voyantjs/admin/components/operator-admin-sidebar"
import {
  createAdminExtensionRegistry,
  defineAdminExtension,
  resolveAdminNavigation,
} from "@voyantjs/admin"

function App() {
  return (
    <AdminProvider defaultTheme="system">
      <Dashboard />
    </AdminProvider>
  )
}

Exports

| Entry | Description | | --- | --- | | . | Barrel re-exports | | ./components/admin-nav-group | Sidebar navigation group renderer | | ./components/admin-nav-link | Navigation link adapter types and default anchor link | | ./components/admin-widget-slot | Widget slot renderer for admin extension widgets | | ./components/operator-admin-bootstrap-gate | Single-tenant-first shell bootstrap gate | | ./components/operator-admin-sidebar | Operator sidebar and workspace layout | | ./components/operator-admin-user-menu | Operator user/account/theme/locale menu | | ./components/team-settings-page | Operator team invitation settings composition | | ./extensions | Admin extension types and helpers | | ./navigation/operator-navigation | Base operator admin navigation factory | | ./providers/admin-provider | AdminProvider composing QueryClient + Theme | | ./providers/locale-preferences | AdminLocalePreferenceSync for user locale/timezone defaults | | ./providers/operator-admin-shell | OperatorAdminShellProvider and provider stack helpers | | ./providers/operator-admin-messages | Operator admin message provider and hooks | | ./providers/theme | ThemeProvider, useTheme with system-theme support | | ./providers/query-client | makeQueryClient(config?) factory with Voyant defaults | | ./lib/initials | getInitials, getDisplayName helpers | | ./types | AdminUser, NavItem, NavSubItem, ThemeMode, AuthActions |

Admin Extensions

Use defineAdminExtension(...) to declare shared admin contributions and keep the extension surface explicit:

import { defineAdminExtension } from "@voyantjs/admin"

export const financeExtension = defineAdminExtension({
  id: "finance-tools",
  navigation: [
    {
      order: 10,
      items: [{ id: "settlements", title: "Settlements", url: "/finance/settlements" }],
    },
  ],
})

Templates can merge those contributions into their base navigation with resolveAdminNavigation(...) and expose widget slots with resolveAdminWidgets(...). When a template wants one explicit source-controlled registry, compose it with createAdminExtensionRegistry(...).

Render widgets from a template-owned registry with AdminWidgetSlotRenderer:

import { AdminWidgetSlotRenderer, createAdminExtensionRegistry } from "@voyantjs/admin"

const adminExtensions = createAdminExtensionRegistry(financeExtension)

function DashboardHeader({ dashboard }) {
  return (
    <AdminWidgetSlotRenderer
      extensions={adminExtensions}
      slot="dashboard.header"
      props={{ dashboard }}
    />
  )
}

The operator template currently exposes these stable slots: dashboard.header, dashboard.after-kpis, dashboard.footer, booking.details.header, booking.details.after-summary, invoice.details.header, and invoice.details.after-summary.

Operator Shell

Operator apps can centralize the standard provider order with OperatorAdminShellProvider. It composes AdminProvider, VoyantReactProvider, operator admin messages, optional app-level providers, and optional domain UI message providers that accept { locale, children }.

import {
  type AdminDomainMessagesProvider,
  OperatorAdminShellProvider,
} from "@voyantjs/admin"
import { BookingsUiMessagesProvider } from "@voyantjs/bookings-ui/i18n"

const domainMessageProviders = [
  BookingsUiMessagesProvider,
] satisfies readonly AdminDomainMessagesProvider[]

function App({ queryClient }: { queryClient: QueryClient }) {
  return (
    <OperatorAdminShellProvider
      baseUrl="/api"
      queryClient={queryClient}
      domainMessageProviders={domainMessageProviders}
    >
      <Dashboard />
    </OperatorAdminShellProvider>
  )
}

Use OperatorAdminWorkspaceLayout to reuse the standard sidebar chrome while keeping app-owned routing and sign-out behavior explicit:

import { Link, useRouterState } from "@tanstack/react-router"
import { OperatorAdminWorkspaceLayout } from "@voyantjs/admin"

const AdminLink = ({ children, href, onClick, target }) => (
  <Link to={href} onClick={onClick} target={target}>
    {children}
  </Link>
)

function Workspace({ children, user }) {
  const currentPath = useRouterState({ select: (s) => s.location.pathname })

  return (
    <OperatorAdminWorkspaceLayout
      currentPath={currentPath}
      linkComponent={AdminLink}
      user={user}
      onSignOut={() => signOut({ redirectTo: "/sign-in" })}
    >
      {children}
    </OperatorAdminWorkspaceLayout>
  )
}

OperatorAdminShellProvider and OperatorAdminWorkspaceLayout do not fetch or require Better Auth organizations. First-party Voyant templates are single-tenant per deployment: load the current authenticated user first, then render the shell with the user prop.

Use OperatorAdminBootstrapGate to make that contract explicit:

<OperatorAdminBootstrapGate user={user} isUserLoading={isLoading}>
  <OperatorAdminWorkspaceLayout user={user}>{children}</OperatorAdminWorkspaceLayout>
</OperatorAdminBootstrapGate>

The workspace layout follows the shadcn sidebar composition with a SidebarInset main region and a visible sidebar trigger in the inset header. Pass variant="inset" or variant="floating" and side="right" when an app needs one of the modern sidebar variants. The sidebar can also be toggled with Cmd+B on macOS or Ctrl+B on Windows and Linux.

Use OperatorAdminPageShell when a route needs the standard per-page header: sidebar trigger, breadcrumbs, page-level actions, and a padded body. Disable the workspace layout's fallback trigger header so the page shell owns the route header:

import { OperatorAdminPageShell, OperatorAdminWorkspaceLayout } from "@voyantjs/admin"

function Workspace({ children }) {
  return (
    <OperatorAdminWorkspaceLayout currentPath="/bookings" showSidebarTrigger={false}>
      {children}
    </OperatorAdminWorkspaceLayout>
  )
}

function BookingsRoute() {
  return (
    <OperatorAdminPageShell
      breadcrumbs={<BreadcrumbTrail current="Bookings" />}
      actions={<NewBookingButton />}
    >
      <BookingsPage />
    </OperatorAdminPageShell>
  )
}

Pass padded={false} for full-bleed pages such as maps, workflow timelines, or canvas-style tools that own their own spacing.

Route-level document titles are derived from navItems and currentPath by default, so /bookings renders Bookings · Voyant and tracks the active locale when the navigation messages change. AdminPageHead also keeps <html lang> synchronized with useLocale().resolvedLocale and updates the description and Open Graph description meta tags when a description is provided. Apps can override detail routes that are not represented by navigation items with useAdminPageHead:

import { useAdminPageHead } from "@voyantjs/admin"

function ProductDetailPage({ product }) {
  useAdminPageHead({
    title: product.name,
    description: product.summary,
  })

  return <ProductDetailLayout product={product} />
}

Set pageHead={false} on OperatorAdminWorkspaceLayout only when an app owns all document metadata itself.

The default brand uses the exported VoyantMark and VoyantWordmark SVG components and swaps from wordmark to mark in collapsed icon mode. Apps that need a custom lockup can pass brand={<MyBrand />} or compose those exported brand components directly.

Workspace switching and team-management routes remain app-owned opt-ins. Apps that intentionally implement workspace switching can opt into mode="organization" and pass their own workspace readiness state.

TeamSettingsPage provides the reusable team invitation page for operator templates. It uses OperatorAdminShellProvider's VoyantReactProvider for API base URL/fetcher resolution and the operator admin message provider for labels; pass the api prop only when an app needs a custom invitation transport.

DashboardPage renders explicit empty states for charts, upcoming departures, and outstanding invoices instead of blank card frames. Brand-new tenants see a first-run onboarding panel. Apps that need different empty copy or actions can pass emptyStates for the affected section:

<DashboardPage
  emptyStates={{
    revenueTrend: {
      title: "No revenue posted yet",
      action: { href: "/reports", label: "Open reports" },
    },
  }}
/>

License

Apache-2.0