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

ptech-shell-ui

v2.14.0

Published

Composable app shell layout, header, routing feedback, and status-page patterns for Module Federation remotes.

Downloads

340

Readme

ptech-shell-ui

Composable app shell layout, navigation feedback, and status-page presentation for PTECH Module Federation remotes.

This package owns layout patterns — not shell service contracts (see ptech-shell-sdk), not MSAL/router adapters (see ptech-shell-react), and not standalone mocks (see ptech-shell-dev).

Install

npm i ptech-shell-ui ptech-shell-sdk react

The root entry is React Router runtime-free. It renders breadcrumb anchors and delegates unmodified app-local clicks through TOKENS.navigation when the host has registered a navigation service. When TOKENS.breadcrumbContext is present, both top-bar entries prepend its host-owned portal/tenant/app items and delegate their unmodified clicks to the host while retaining native href fallbacks. Consumers that deliberately want a direct React Router Link integration can also install react-router and import:

import { ShellTopBar } from 'ptech-shell-ui/react-router';

Quick start

import { Suspense } from 'react';
import { Routes, Route } from 'react-router';
import {
  AppShellLayout,
  HeaderProvider,
  ShellTopBar,
  useHeaderConfig,
  NotFoundPage,
  useRouteBreadcrumbs,
} from 'ptech-shell-ui';
import { Home } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { ROUTE_META } from './config/routeConfig';
import { AppSidebar } from './AppSidebar';
import { UserMenu } from './UserMenu';

function DashboardPage() {
  const { t } = useTranslation();
  const breadcrumbs = useRouteBreadcrumbs({
    routeMeta: ROUTE_META,
    translate: t,
    homeIcon: Home,
  });

  useHeaderConfig({
    variant: 'breadcrumb',
    breadcrumbs,
    currentIcon: Home,
    currentTitle: t('nav.dashboard'),
  });

  return <div>...</div>;
}

export function AppShell({ children }: { children?: React.ReactNode }) {
  return (
    <HeaderProvider>
      <AppShellLayout
        sidebar={<AppSidebar />}
        header={
          <ShellTopBar
            trailing={<UserMenu />}
            classNames={{
              root: 'h-12 border-b border-ps-header bg-ps-header backdrop-blur-sm',
              breadcrumbLink: 'text-ps-subtle hover:text-ps-aqua transition-colors truncate',
              breadcrumbCurrent: 'flex items-center gap-1.5 shrink-0 font-semibold text-ps whitespace-nowrap',
              breadcrumbCurrentIcon: 'w-4 h-4 text-ps-aqua',
            }}
          />
        }
      >
        <Suspense fallback={null}>
          <Routes>
            <Route path="dashboard" element={<DashboardPage />} />
          </Routes>
        </Suspense>
      </AppShellLayout>
    </HeaderProvider>
  );
}

Shared status surfaces

StatusPage is the shared, responsive presentation primitive for full-page navigation and availability feedback. The package also exports semantic wrappers for recurring shell states:

| Component | Default marker | Intended state | | --- | --- | --- | | NotFoundPage | 404 | The owning router cannot match a route | | AccessDeniedPage | 403 | The signed-in user lacks tenant, app, organization, role, or permission access | | AuthenticationRequiredPage | 401 | Sign-in or session renewal is required | | UnexpectedErrorPage | 500 | An error boundary or request failed unexpectedly | | ServiceUnavailablePage | 503 | An API, dependency, or maintenance window is temporarily unavailable | | AppUnavailablePage | none | A Module Federation remote did not reach a usable screen | | OfflinePage | none | Connectivity checks confirm that the client is offline | | LoadingStatusPage | none | A full-page transition is still preparing consumer-owned content |

The wrappers only select a semantic component name and, where applicable, a default numeric marker. Consumers still own routing, authorization, MSAL, connectivity detection, launch deadlines, retry behavior, and support reporting. Use StatusPage directly for a status that does not match one of the wrappers.

RemoteBoundary catches render and remote-mount failures without imposing a brand. Supply either a localized fallback node or (error, reset) => ReactNode; changing resetKey clears a captured failure. useDismissiblePopover standardizes outside-pointer/Escape dismissal, while useDrawerA11y supplies dialog props, focus entry/trapping, Escape handling, and focus restoration.

All copy comes from the consumer so applications can use their own i18n layer. The first direct action child receives primary treatment; subsequent links or buttons use the secondary style. detail is suitable for a safe pathname, trace ID, or correlation ID; never pass a raw server error, access token, credential, secret-bearing URL, or PII.

Route miss

import { Link, Route, Routes, useLocation } from 'react-router';
import { NotFoundPage } from 'ptech-shell-ui';
import { useTranslation } from 'react-i18next';

function RouteNotFound() {
  const { pathname } = useLocation();
  const { t } = useTranslation();

  return (
    <NotFoundPage
      eyebrow={t('errors.notFound.eyebrow')}
      title={t('errors.notFound.title')}
      description={t('errors.notFound.description')}
      detail={pathname}
      actions={
        <>
          <Link to="/">{t('errors.notFound.goHome')}</Link>
          <button type="button" onClick={() => window.history.back()}>
            {t('errors.notFound.goBack')}
          </button>
        </>
      }
    />
  );
}

export function AppRoutes() {
  return (
    <Routes>
      <Route path="dashboard" element={<DashboardPage />} />
      <Route path="*" element={<RouteNotFound />} />
    </Routes>
  );
}

Hosts and remotes should each keep a catch-all route at the boundary they own. NotFoundPage supplies the presentation without deciding which router owns the miss.

Access and availability

import { Link } from 'react-router';
import { useTranslation } from 'react-i18next';
import {
  AccessDeniedPage,
  AppUnavailablePage,
  UnexpectedErrorPage,
} from 'ptech-shell-ui';

export function OrganizationAccessDenied() {
  const { t } = useTranslation();

  return (
    <AccessDeniedPage
      eyebrow={t('errors.accessDenied.eyebrow')}
      title={t('errors.accessDenied.title')}
      description={t('errors.accessDenied.description')}
      actions={
        <Link to="/apps">{t('errors.accessDenied.backToApps')}</Link>
      }
    />
  );
}

export function RemoteLaunchFailed({
  retry,
  supportTraceId,
}: {
  retry: () => void;
  supportTraceId?: string;
}) {
  const { t } = useTranslation();

  return (
    <AppUnavailablePage
      title={t('errors.appUnavailable.title')}
      description={t('errors.appUnavailable.description')}
      detail={supportTraceId}
      actions={
        <button type="button" onClick={retry}>
          {t('errors.actions.retry')}
        </button>
      }
    />
  );
}

export function ErrorBoundaryFallback({ supportTraceId }: { supportTraceId?: string }) {
  const { t } = useTranslation();

  return (
    <UnexpectedErrorPage
      title={t('errors.unexpected.title')}
      description={t('errors.unexpected.description')}
      detail={supportTraceId}
    />
  );
}

Do not use OfflinePage for every network exception: render it only after a connectivity check confirms an offline client. A reachable shell with an unavailable API should use ServiceUnavailablePage. Maintenance is a service-unavailable variant, not a separate shared page.

All status components use PTECH semantic CSS variables when available and include standalone fallbacks. Consumers may override custom properties through style or a wrapper class and may replace the default artwork through artwork. ptech-status-page is the generic root class; the legacy ptech-not-found class and --ptech-nf-* custom properties remain present for backward-compatible NotFoundPage theming.

Standalone bootstrap

Pair with initStandaloneApp from ptech-shell-dev and MSAL from ptech-shell-react:

import { initStandaloneApp } from 'ptech-shell-dev';
import { createMsalUserService } from 'ptech-shell-react';

initStandaloneApp({
  appKey: 'my-remote',
  apiBase: import.meta.env.PUBLIC_API_URL,
  tenant: { tenantId: '...', tenantSlug: 'dev' },
  createUserService: () =>
    createMsalUserService({
      msal: msalInstance,
      defaultScopes: ['api://example/access_as_user'],
      loginMode: 'redirect',
    }),
  defaultAuthScopes: ['api://example/access_as_user'],
});

Exports

| Export | Purpose | |--------|---------| | AppShellLayout | Sidebar + header slots, scrollable content, --app-sidebar-width CSS var | | HeaderProvider / useHeaderConfig | Page-driven top bar state | | useHeaderActions | Inject header actions from nested components | | ShellTopBar | Breadcrumb/overview top bar driven by header context | | StatusPage | Base responsive, theme-aware status presentation with consumer-owned copy and actions | | NotFoundPage | Route-miss status surface with a default 404 marker | | AccessDeniedPage / AuthenticationRequiredPage | Presentational access and sign-in-required status surfaces | | UnexpectedErrorPage / ServiceUnavailablePage | Presentational unexpected and temporary service failure surfaces | | AppUnavailablePage / OfflinePage | Presentational remote-launch and confirmed-offline status surfaces | | useRouteBreadcrumbs | Build parent crumbs from a route meta map | | useAppPath / useAppPathResolver | Basename-aware hrefs via shell NavigationService |

The optional ./react-router subpath exports a React Router-backed ShellTopBar with the same public props. Only that subpath imports react-router; the root entry remains usable without it.

useAppPath, useAppPathResolver, and useCurrentAppPath subscribe to both the SDK registry and the active navigation service. A component mounted before TOKENS.navigation is registered therefore re-renders as soon as the host or standalone bootstrap registers it; no remount is required. During static/server rendering the hooks use the registry snapshot available for that render and fall back to app-relative paths when no navigation service exists.

Build

npm run build -w ptech-shell-ui
npm run test -w ptech-shell-ui

Tests cover static rendering through react-dom/server and late service registration through React 19 act plus jsdom. react-dom and jsdom are development-only dependencies; the published UI runtime still relies only on its existing peers.