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

@mdxui/app

v6.0.0

Published

Abstract application framework layer for building admin interfaces and SaaS applications with React. Provides a "Bring Your Own Backend" architecture where you implement the data and auth providers while the framework handles UI composition, navigation, t

Readme

@mdxui/app

Abstract application framework layer for building admin interfaces and SaaS applications with React. Provides a "Bring Your Own Backend" architecture where you implement the data and auth providers while the framework handles UI composition, navigation, theming, and resource management.

Installation

npm install @mdxui/app @mdxui/primitives
# or
pnpm add @mdxui/app @mdxui/primitives
# or
yarn add @mdxui/app @mdxui/primitives

Quick Start

import { App, AppShell, NavMain, NavUser, Resource } from '@mdxui/app'
import type { DataProvider, AuthProvider } from '@mdxui/app'
import { Users, Package } from 'lucide-react'

// 1. Implement your data provider
const dataProvider: DataProvider = {
  getList: async (resource, params) => {
    const response = await fetch(`/api/${resource}`)
    const data = await response.json()
    return { data: data.items, total: data.total }
  },
  getOne: async (resource, { id }) => {
    const response = await fetch(`/api/${resource}/${id}`)
    return { data: await response.json() }
  },
  getMany: async (resource, { ids }) => {
    const response = await fetch(`/api/${resource}?ids=${ids.join(',')}`)
    return { data: await response.json() }
  },
  create: async (resource, { data }) => {
    const response = await fetch(`/api/${resource}`, {
      method: 'POST',
      body: JSON.stringify(data),
    })
    return { data: await response.json() }
  },
  update: async (resource, { id, data }) => {
    const response = await fetch(`/api/${resource}/${id}`, {
      method: 'PUT',
      body: JSON.stringify(data),
    })
    return { data: await response.json() }
  },
  delete: async (resource, { id }) => {
    const response = await fetch(`/api/${resource}/${id}`, { method: 'DELETE' })
    return { data: await response.json() }
  },
}

// 2. Implement your auth provider
const authProvider: AuthProvider = {
  login: async ({ username, password }) => {
    await fetch('/api/auth/login', {
      method: 'POST',
      body: JSON.stringify({ username, password }),
    })
  },
  logout: async () => {
    await fetch('/api/auth/logout', { method: 'POST' })
  },
  checkAuth: async () => {
    const response = await fetch('/api/auth/me')
    if (!response.ok) throw new Error('Not authenticated')
  },
  checkError: async (error) => {
    if (error.message === 'Unauthorized') throw error
  },
  getIdentity: async () => {
    const response = await fetch('/api/auth/me')
    return response.json()
  },
  getPermissions: async () => {
    const response = await fetch('/api/auth/permissions')
    return response.json()
  },
}

// 3. Define your resources
const resources = [
  { name: 'users', label: 'Users', icon: Users, list: UsersList, edit: UserEdit },
  { name: 'products', label: 'Products', icon: Package, list: ProductsList },
]

// 4. Build your app
function MyAdmin() {
  return (
    <App
      config={{ name: 'My Admin', basePath: '/admin' }}
      dataProvider={dataProvider}
      authProvider={authProvider}
      resources={resources}
    >
      <AppShell
        config={{ name: 'My Admin' }}
        nav={<NavMain />}
        footer={<NavUser />}
      >
        {/* Your routes/content here */}
      </AppShell>
    </App>
  )
}

Core Concepts

Bring Your Own Backend

@mdxui/app doesn't dictate your backend. You implement two interfaces:

  • DataProvider: 6-method interface for CRUD operations
  • AuthProvider: 6-method interface for authentication

This lets you connect to any API, database, or backend service.

Resources

Resources are the core building blocks. Each resource represents an entity (users, products, orders) that can be listed, created, edited, and deleted.

const usersResource: ResourceDefinition = {
  name: 'users',           // Used in URLs and data provider calls
  label: 'User',           // Singular label
  labelPlural: 'Users',    // Plural label
  icon: Users,             // Lucide icon for navigation
  list: UsersList,         // List view component
  show: UserShow,          // Detail view component
  create: UserCreate,      // Create form component
  edit: UserEdit,          // Edit form component
}

Provider Composition

The App component composes all necessary providers in the correct order:

ThemeProvider
  └─ NavigationProvider
       └─ DataProviderProvider
            └─ AuthProvider
                 └─ ResourcesProvider
                      └─ AppProvider
                           └─ Your Content

API Reference

Providers

DataProviderProvider & useDataProvider

Provides data access throughout your application.

import { DataProviderProvider, useDataProvider } from '@mdxui/app'

// Wrap your app
<DataProviderProvider dataProvider={myDataProvider}>
  <App />
</DataProviderProvider>

// Use in components
function UsersList() {
  const dataProvider = useDataProvider()

  useEffect(() => {
    dataProvider.getList('users', {
      pagination: { page: 1, perPage: 10 },
      sort: { field: 'name', order: 'ASC' },
      filter: { status: 'active' },
    }).then(({ data, total }) => {
      // Handle data
    })
  }, [])
}

DataProvider Interface:

| Method | Params | Returns | |--------|--------|---------| | getList | { pagination?, sort?, filter? } | { data: T[], total?, pageInfo? } | | getOne | { id } | { data: T } | | getMany | { ids } | { data: T[] } | | create | { data } | { data: T } | | update | { id, data, previousData? } | { data: T } | | delete | { id, previousData? } | { data: T } |

AuthProvider & useAuth

Manages authentication state and operations.

import { AuthProvider, useAuth } from '@mdxui/app'

// Wrap your app
<AuthProvider authProvider={myAuthProvider}>
  <App />
</AuthProvider>

// Use in components
function LoginPage() {
  const { login, isLoading } = useAuth()

  const handleSubmit = async (data) => {
    await login({ username: data.email, password: data.password })
  }
}

function Dashboard() {
  const { isAuthenticated, identity, logout, permissions } = useAuth()

  if (!isAuthenticated) {
    return <Redirect to="/login" />
  }

  return <div>Welcome, {identity?.fullName}!</div>
}

AuthProvider Interface:

| Method | Purpose | |--------|---------| | login(params) | Authenticate user with credentials | | logout() | End user session | | checkAuth() | Verify current auth state (throws if not authenticated) | | checkError(error) | Handle auth errors (e.g., 401 responses) | | getIdentity() | Get current user info | | getPermissions() | Get user permissions/roles |

useAuth Return Value:

interface AuthContextValue {
  isAuthenticated: boolean
  identity?: UserIdentity
  permissions?: string[]
  isLoading: boolean
  login: (params: LoginParams) => Promise<void>
  logout: () => Promise<void>
  refreshAuth: () => Promise<void>
}

NavigationProvider & useNavigation

Integrates with your routing library (react-router, Next.js, etc.).

import { NavigationProvider, useNavigation } from '@mdxui/app'
import { Link as RouterLink, useNavigate, useLocation } from 'react-router-dom'

// Create adapter for react-router
const navigationProvider = {
  navigate: (to, options) => navigate(to, options),
  getCurrentPath: () => location.pathname,
  LinkComponent: ({ href, children, className }) => (
    <RouterLink to={href} className={className}>{children}</RouterLink>
  ),
}

<NavigationProvider navigationProvider={navigationProvider}>
  <App />
</NavigationProvider>

// Use in components
function MyComponent() {
  const { navigate, getCurrentPath, Link } = useNavigation()

  return (
    <div>
      <Link href="/dashboard">Go to Dashboard</Link>
      <button onClick={() => navigate('/settings')}>Settings</button>
    </div>
  )
}

Default Behavior: If no navigationProvider is passed, uses window.location for navigation and plain <a> tags for links.

ThemeProvider & useTheme

Manages light/dark theme with system preference detection.

import { ThemeProvider, useTheme } from '@mdxui/app'

<ThemeProvider defaultTheme="system" storageKey="my-app-theme">
  <App />
</ThemeProvider>

// Use in components
function ThemeToggle() {
  const { theme, setTheme, resolvedTheme, toggleTheme } = useTheme()

  return (
    <button onClick={toggleTheme}>
      {resolvedTheme === 'dark' ? 'Switch to Light' : 'Switch to Dark'}
    </button>
  )
}

Theme Options: 'light' | 'dark' | 'system'

useTheme Return Value:

interface ThemeContextValue {
  theme: Theme                    // Current setting (may be 'system')
  setTheme: (theme: Theme) => void
  resolvedTheme: 'light' | 'dark' // Actual applied theme
  toggleTheme: () => void
}

ResourcesProvider & useResources

Global registry of all resources in the application.

import { ResourcesProvider, useResources } from '@mdxui/app'

<ResourcesProvider resources={[usersResource, productsResource]}>
  <App />
</ResourcesProvider>

// Use in components
function ResourceNav() {
  const { resources, getResource } = useResources()

  return (
    <nav>
      {resources.map(r => (
        <Link key={r.name} href={`/${r.name}`}>
          {r.icon && <r.icon />}
          {r.labelPlural ?? r.name}
        </Link>
      ))}
    </nav>
  )
}

useResources Return Value:

interface ResourcesContextValue {
  resources: ResourceDefinition[]
  getResource: (name: string) => ResourceDefinition | undefined
  registerResource: (resource: ResourceDefinition) => void
  unregisterResource: (name: string) => void
}

ResourceProvider & useResource

Context for a single resource view (list, show, edit, create).

import { ResourceProvider, useResource } from '@mdxui/app'

<ResourceProvider resource="users" definition={usersResource} id={userId}>
  <UserEdit />
</ResourceProvider>

// Use in components
function UserEdit() {
  const { resource, id, hasEdit, definition } = useResource()

  // resource = 'users'
  // id = userId
  // hasEdit = true (if edit component defined)
}

useResource Return Value:

interface ResourceContextValue {
  resource: string
  definition?: ResourceDefinition
  id?: Identifier
  hasList: boolean
  hasCreate: boolean
  hasEdit: boolean
  hasShow: boolean
}

Components

App

Root component that composes all providers. Use this as the outermost wrapper.

import { App } from '@mdxui/app'

<App
  config={{ name: 'Admin', basePath: '/admin', logo: <Logo /> }}
  dataProvider={dataProvider}
  authProvider={authProvider}
  resources={resources}
  navigationProvider={routerAdapter}  // Optional
  defaultTheme="system"               // Optional
  layout={CustomLayout}               // Optional wrapper
>
  {children}
</App>

Props:

| Prop | Type | Required | Description | |------|------|----------|-------------| | config | AppConfig | Yes | App name, basePath, logo, description | | dataProvider | DataProvider | Yes | Your data provider implementation | | authProvider | AuthProvider | Yes | Your auth provider implementation | | resources | ResourceDefinition[] | No | Resource definitions | | navigationProvider | NavigationProvider | No | Router integration | | defaultTheme | Theme | No | Default theme ('light', 'dark', 'system') | | themeStorageKey | string | No | localStorage key for theme | | layout | ComponentType | No | Custom layout wrapper |

Resource

Declarative resource registration. Can be used instead of or alongside the resources prop on App.

import { Resource } from '@mdxui/app'

<App dataProvider={dp} authProvider={ap} config={config}>
  <Resource
    name="users"
    label="User"
    labelPlural="Users"
    icon={Users}
    list={UsersList}
    show={UserShow}
    create={UserCreate}
    edit={UserEdit}
  />
  <Resource
    name="products"
    label="Product"
    labelPlural="Products"
    icon={Package}
    list={ProductsList}
  />
</App>

Resources are automatically registered on mount and unregistered on unmount.

AppShell

Main layout component with sidebar navigation.

import { AppShell, NavMain, NavUser, AppBreadcrumbs } from '@mdxui/app'

<AppShell
  config={{ name: 'Admin' }}
  navigation={navGroups}
  user={currentUser}
  header={<TeamSwitcher />}
  nav={<NavMain />}
  footer={<NavUser />}
  pageHeader={<AppBreadcrumbs basePath="/admin" />}
  collapsible="icon"     // 'offcanvas' | 'icon' | 'none'
  variant="inset"        // 'sidebar' | 'floating' | 'inset'
>
  {children}
</AppShell>

Props:

| Prop | Type | Default | Description | |------|------|---------|-------------| | config | AppConfig | - | App configuration | | navigation | NavGroup[] | [] | Navigation groups | | user | UserIdentity | - | Current user | | header | ReactNode | - | Sidebar header content | | nav | ReactNode | - | Sidebar navigation content | | footer | ReactNode | - | Sidebar footer content | | pageHeader | ReactNode | - | Page header (breadcrumbs) | | collapsible | string | 'icon' | Collapse behavior | | variant | string | 'inset' | Sidebar style variant |

NavMain

Renders navigation groups in the sidebar.

import { NavMain } from '@mdxui/app'

// Single group
<NavMain
  label="Platform"
  items={[
    { title: 'Dashboard', url: '/admin', icon: LayoutDashboard },
    {
      title: 'Settings',
      url: '/admin/settings',
      icon: Settings,
      items: [
        { title: 'General', url: '/admin/settings/general' },
        { title: 'Team', url: '/admin/settings/team' },
      ],
    },
  ]}
/>

// Multiple groups
<NavMain
  groups={[
    { label: 'Main', items: [...] },
    { label: 'Settings', items: [...] },
  ]}
/>

NavUser

User menu in the sidebar footer with dropdown actions.

import { NavUser } from '@mdxui/app'

<NavUser
  accountPath="/admin/settings"
  billingPath="/admin/settings/billing"
  showThemeToggle
  showNotifications
  actions={[
    { label: 'API Keys', icon: Key, href: '/admin/settings/api-keys' },
    { label: 'Support', icon: HelpCircle, onClick: openSupport },
  ]}
/>

PageHeader

Header area for pages with breadcrumbs, title, and actions.

import { PageHeader } from '@mdxui/app'

<PageHeader
  breadcrumbs={<AppBreadcrumbs />}
  title="Users"
  description="Manage user accounts"
  actions={
    <Button>
      <Plus className="mr-2 h-4 w-4" />
      Add User
    </Button>
  }
/>

AppBreadcrumbs

Auto-generates breadcrumbs from the current URL path.

import { AppBreadcrumbs } from '@mdxui/app'

// Auto-generate from path
<AppBreadcrumbs basePath="/admin" />

// With custom labels
<AppBreadcrumbs
  basePath="/admin"
  labels={{ 'api-keys': 'API Keys', team: 'Team Members' }}
/>

// Custom items
<AppBreadcrumbs
  items={[
    { label: 'Dashboard', href: '/admin' },
    { label: 'Users', href: '/admin/users' },
    { label: 'John Doe' },
  ]}
/>

TypeScript Types

All types are exported for use in your application:

import type {
  // Core
  Identifier,
  RaRecord,

  // Auth
  UserIdentity,
  AuthProvider,
  LoginParams,

  // Navigation
  NavItem,
  NavGroup,
  BreadcrumbItemData,
  UserMenuAction,
  LinkProps,
  NavigationProvider,

  // Data
  DataProvider,
  GetListParams,
  GetListResult,
  GetOneParams,
  GetOneResult,
  GetManyParams,
  GetManyResult,
  CreateParams,
  CreateResult,
  UpdateParams,
  UpdateResult,
  DeleteParams,
  DeleteResult,

  // Resources
  ResourceDefinition,
  ResourceContextValue,

  // Config
  AppConfig,
  Theme,
} from '@mdxui/app'

Integration with @mdxui/admin

@mdxui/app provides the abstract framework layer. @mdxui/admin extends it with concrete UI components for admin interfaces:

import { App } from '@mdxui/app'
import { List, Datagrid, TextField, EditButton } from '@mdxui/admin'

function UsersList() {
  return (
    <List>
      <Datagrid>
        <TextField source="name" />
        <TextField source="email" />
        <EditButton />
      </Datagrid>
    </List>
  )
}

<App dataProvider={dp} authProvider={ap} config={config}>
  <Resource name="users" list={UsersList} />
</App>

Integration with @mdxui/do

For applications using .do domains and worker-based backends:

import { App } from '@mdxui/app'
import { createDoDataProvider, DoProvider } from '@mdxui/do'

const doDataProvider = createDoDataProvider({
  apiUrl: 'https://api.example.do',
})

<DoProvider>
  <App
    dataProvider={doDataProvider}
    authProvider={doAuthProvider}
    config={{ name: 'My App' }}
  >
    {children}
  </App>
</DoProvider>

Package Exports

The package provides subpath exports for tree-shaking:

// Main entry (all exports)
import { App, useAuth, useDataProvider } from '@mdxui/app'

// Individual components
import { App } from '@mdxui/app/app'
import { Resource } from '@mdxui/app/resource'
import { AppShell } from '@mdxui/app/shell'
import { NavMain } from '@mdxui/app/nav-main'
import { NavUser } from '@mdxui/app/nav-user'
import { PageHeader } from '@mdxui/app/page-header'
import { AppBreadcrumbs } from '@mdxui/app/breadcrumbs'

// Context providers and hooks
import { useAuth, useDataProvider, useNavigation } from '@mdxui/app/context'

// Type definitions only
import type { DataProvider, AuthProvider } from '@mdxui/app/types'

License

MIT