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

@mounaji_npm/saas-template

v0.4.2

Published

Complete SaaS app shell with module-registry nav, sidebar, topbar and page modules — install once, add pages incrementally

Readme

@mounaji_npm/saas-template

Complete SaaS app shell with sidebar, topnav, and module registry. Install once — add pages incrementally by registering module manifests.

This package is part of the app setup layer, not the CLI layer. It gives you the runtime shell and starter module manifests inside an existing app. Project scaffolding stays in @mounaji_npm/cli.


Install

npm install @mounaji_npm/tokens @mounaji_npm/core @mounaji_npm/saas-template

Recommended pairing:

  • @mounaji_npm/core for providers, bootstrap, guards, capability registry, and gates
  • @mounaji_npm/saas-template for AppShell, sidebar/topnav, and module manifests
  • @mounaji_npm/cli only when scaffolding a brand new project

Quick Start

Next.js App Router

// app/providers.jsx
'use client';
import { useState } from 'react';
import { usePathname } from 'next/navigation';
import Link from 'next/link';
import { AppShell } from '@mounaji_npm/saas-template';
import { CORE_MODULES } from '@mounaji_npm/saas-template/modules';

export function Providers({ children }) {
  const pathname = usePathname();
  const [dark, setDark] = useState(true);

  return (
    <AppShell
      modules={CORE_MODULES}
      activePath={pathname}
      LinkComponent={Link}
      isDark={dark}
      onThemeToggle={() => setDark(d => !d)}
    >
      {children}
    </AppShell>
  );
}

// app/layout.js
import { Providers } from './providers';

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  );
}

Vite + React

// src/App.jsx
import { useState } from 'react';
import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom';
import { AppShell } from '@mounaji_npm/saas-template';
import { CORE_MODULES } from '@mounaji_npm/saas-template/modules';
import DashboardPage from './pages/DashboardPage';
import ChatPage from './pages/ChatPage';

function AppInner() {
  const { pathname } = useLocation();
  const [dark, setDark] = useState(true);

  return (
    <AppShell
      modules={CORE_MODULES}
      activePath={pathname}
      isDark={dark}
      onThemeToggle={() => setDark(d => !d)}
    >
      <Routes>
        <Route path="/"          element={<DashboardPage />} />
        <Route path="/chat"      element={<ChatPage />} />
      </Routes>
    </AppShell>
  );
}

export default function App() {
  return <BrowserRouter><AppInner /></BrowserRouter>;
}

AppShell Props

| Prop | Type | Default | Description | |---|---|---|---| | modules | ModuleManifest[] | [] | Nav modules shown in the sidebar (main section) | | bottomModules | ModuleManifest[] | [SETTINGS_MODULE, HELP_MODULE] | Nav items pinned to bottom | | activePath | string | '/' | Current route — highlights the matching nav item | | onNavigate | function | — | (path: string) => void — used when LinkComponent is not provided | | LinkComponent | component | 'a' | Router link component (Link from Next.js or React Router) | | logo | ReactNode | default M | Logo in the sidebar header | | org | object | — | { name: string, logo?: string } — shown in TopNav breadcrumb | | project | object | — | { name: string } — shown in TopNav breadcrumb | | onOrgClick | function | — | Called when org name is clicked | | onProjectClick | function | — | Called when project name is clicked | | onNewProject | function | — | Called when "New Project" button is clicked | | user | object | — | { name, email, avatar? } — shown in TopNav user menu | | onLogout | function | — | Called from user menu logout button | | isDark | boolean | true | Dark/light theme | | onThemeToggle | function | — | Called by theme toggle button in TopNav | | topNavLeft | ReactNode | — | Extra content in TopNav left slot | | topNavCenter | ReactNode | — | Extra content in TopNav center slot | | topNavRight | ReactNode | — | Extra content in TopNav right slot | | tokens | object | — | Token overrides merged with DEFAULT_TOKENS | | tokenPersist | boolean | true | Persist token overrides to localStorage | | tokenStorageKey | string | 'mn_tokens' | localStorage key for tokens | | i18n | object | — | See i18n integration |


Module Manifests

Import pre-built manifests from @mounaji_npm/saas-template/modules:

import {
  // Individual manifests
  HOME_MODULE,
  DASHBOARD_MODULE,
  CHAT_MODULE,
  DISCOVER_MODULE,
  ASSISTANTS_MODULE,
  KNOWLEDGE_BASE_MODULE,
  AGENT_WORKSPACE_MODULE,
  INVENTORY_MODULE,
  PLATFORMS_MODULE,
  WORKFLOWS_MODULE,
  TASKS_MODULE,
  CONNECTIONS_MODULE,
  EMAIL_MODULE,
  CONTACTS_MODULE,
  PRICING_MODULE,
  API_KEYS_MODULE,
  SETTINGS_MODULE,
  HELP_MODULE,

  // Bundle presets
  ALL_MODULES,    // all 16 platform modules
  CORE_MODULES,   // Home + Dashboard + Chat + Assistants + Knowledge Base + Settings
  AI_BUNDLE,      // Chat + Assistants + Knowledge Base + Agent Workspace
  OPS_BUNDLE,     // Workflows + Tasks + Connections + Platforms + Email
} from '@mounaji_npm/saas-template/modules';

Manifest shape

{
  id:      string,    // unique key, used as route key and React list key
  label:   string,    // display name in sidebar
  icon:    string,    // emoji or icon component
  path:    string,    // route path — must match your router
  section: string,    // sidebar group label (e.g. 'Workspace', 'Tools')
  order:   number,    // sort order within section (lower = higher up)
}

Adding a Custom Page

Step 1 — Define a manifest:

// src/modules/analytics-module.js
export const ANALYTICS_MODULE = {
  id:      'analytics',
  label:   'Analytics',
  icon:    '📊',
  path:    '/analytics',
  section: 'Workspace',
  order:   15,
};

Step 2 — Register it in AppShell:

import { ANALYTICS_MODULE } from './modules/analytics-module.js';
import { CORE_MODULES } from '@mounaji_npm/saas-template/modules';

<AppShell modules={[...CORE_MODULES, ANALYTICS_MODULE]}>
  {children}
</AppShell>

Step 3 — Create the page:

// app/analytics/page.js (Next.js) or src/pages/AnalyticsPage.jsx (Vite)
export default function AnalyticsPage() {
  return <div>Analytics content</div>;
}

Dynamic Module Registration (Plugin Pattern)

Register modules conditionally based on user role, plan, or feature flags. The module is added to the sidebar when the component mounts and removed when it unmounts.

import { useRegisterModule } from '@mounaji_npm/saas-template';

function AnalyticsPage() {
  useRegisterModule({
    id:      'analytics',
    label:   'Analytics',
    icon:    '📊',
    path:    '/analytics',
    section: 'Workspace',
    order:   15,
  });

  return <div>Analytics content</div>;
}

Useful for:

  • Role-gated pages (only admins see "Admin Panel")
  • Plan-gated features (only Pro users see "Advanced Analytics")
  • Lazy-loaded feature modules

Module Registry Hooks

import {
  useModuleRegistry,
  useNavTree,
  useRegisterModule,
  ModuleRegistryProvider,
} from '@mounaji_npm/saas-template';

// Read all registered modules
function DebugNav() {
  const { modules } = useModuleRegistry();
  return <pre>{JSON.stringify(modules, null, 2)}</pre>;
}

// Get sorted sidebar tree (grouped by section, ordered by order)
function CustomSidebar() {
  const navTree = useNavTree();
  return navTree.map(section => (
    <div key={section.label}>
      <h3>{section.label}</h3>
      {section.items.map(item => <a key={item.id} href={item.path}>{item.icon} {item.label}</a>)}
    </div>
  ));
}

Pre-built Pages

Import ready-made page components from @mounaji_npm/saas-template/modules:

import { DashboardPage, SettingsPage } from '@mounaji_npm/saas-template/modules';

// app/dashboard/page.js
export default DashboardPage;

// app/settings/page.js — includes live TokenEditor + export/import panels
export default SettingsPage;

i18n Integration

Pass the i18n prop to AppShell to activate I18nProvider and inject LanguageSwitcher into the TopNav:

<AppShell
  modules={CORE_MODULES}
  activePath={pathname}
  i18n={{
    enabled:       true,
    defaultLocale: 'en',
    locales:       ['en', 'es', 'pt'],
    show:          true,
  }}
>
  {children}
</AppShell>

| i18n field | Type | Default | Description | |---|---|---|---| | enabled | boolean | false | Activate i18n | | defaultLocale | string | 'en' | Initial locale | | locales | string[] | all | Available locales | | show | boolean | true | Show LanguageSwitcher in TopNav | | translations | object | — | Custom translation overrides |


Branding with mn-config.js

When scaffolded via @mounaji_npm/cli, a mn-config.js file is generated at the project root for branding and token overrides:

// mn-config.js
export default {
  appName:  'My SaaS',
  logo:     null,                    // replace with <img src="..." /> or SVG
  tokens: {
    colorPrimary:  '#7C3AED',
    colorAccent:   '#06B6D4',
    fontFamily:    '"Geist", system-ui, sans-serif',
    radiusMd:      '10px',
  },
  modules:  [],                      // extra custom module manifests
};

Import and use in your layout:

import config from '../mn-config.js';
import { CORE_MODULES } from '@mounaji_npm/saas-template/modules';

<AppShell
  modules={[...CORE_MODULES, ...config.modules]}
  tokens={config.tokens}
  logo={config.logo}
>
  {children}
</AppShell>