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

@campminder/campminder-shell-react

v0.3.2

Published

> Published on public npm. Install and render: [`docs/consuming-the-shell.md`](../../docs/consuming-the-shell.md). > Working in here: [`CONTRIBUTING.md`](../../CONTRIBUTING.md).

Readme

@campminder/campminder-shell-react

Published on public npm. Install and render: docs/consuming-the-shell.md. Working in here: CONTRIBUTING.md.

The React face of the Campminder shell — the "maximally React-friendly wrapper" ADR D3 prescribes.

It wraps the shell, not the chrome, and that distinction is load-bearing: this package depends on @campminder/campminder-shell, not on @campminder/components. That is why both the package and its directory are portal-scoped — a CampInTouch portal gets its own @campminder/campintouch-shell-react beside this one, not a shared generic wrapper.

Port from: core-js/packages/navigation (campminder-navigation v0.0.1)

This one is unusual: it already exists and already ships. Three core-js apps (reporting, admin, communications) consume it today via workspace:*. It is unpublishable by a single word — "private": true — which is the only reason it is not a dependency already.

That makes #25850 a publish, not a build.

Port inventory

Coming across: AppLayout, TopBar, SideNav, SuperUserBanner, and the useLoadScript / useCreateElement hooks.

Strip on the way in

  • data.ts does not come across. 37,556 bytes on three lines — a hardcoded fixture with real test-tenant PII baked in. This is defect D1. The tree is passed in as configuration.
  • DOM sniffing in AppLayout — it reads hasSideNav off the DOM to compute leftOffsetPx. The shell knows which chrome it composed; nothing needs to ask the document.
  • The QA default URL — see the Phase 8 exit criteria in the technical plan.

Why the name changed

The source package is unscoped and private (campminder-navigation). Publishing needs a scope, and -react says what it is: the React face of chrome that is canonically Lit.

Floor 9 — the public surface

Assessed 2026-09-08 as fresh composition that borrows the hook knowledge, not a copy of AppLayout — four of that component's five files exist only to solve CDN script loading, which stops being a problem once the chrome is a real dependency. What ships here is <CampminderShell> and useWebComponentProperty, sized to make five real consumer defects (found adopting this shell in reports, AB#25851) impossible to reproduce:

'use client'
import {useRef} from 'react'
import {CampminderShell, useWebComponentProperty} from '@campminder/campminder-shell-react'
import type {CampminderAppShell} from '@campminder/campminder-shell'

function Page() {
  const navRef = useRef<HTMLElement & {tree: unknown}>(null)
  useWebComponentProperty(navRef, 'tree', CAMPMINDER_TREE)

  return (
    <CampminderShell portal="campminder" brandColor="#5B2D8E">
      <cm-side-nav ref={navRef} slot="side-nav" />
      <main>…page content…</main>
    </CampminderShell>
  )
}
  • Browser-only loading of both packages (finding 1). <CampminderShell> never statically imports @campminder/campminder-shell for its runtime value — only its types, which TypeScript erases. The real import is a dynamic import() inside an effect, so the module is safe to import on the server; it renders null until that import resolves. Registering the shell's custom element also registers the rest of the chrome, because the shell itself now imports @campminder/components as a side effect (Floor 10) — one import() here is enough for both packages.
  • tree as a property, never a JSX attribute (findings 2 and 3). cm-side-nav's tree is {attribute: false} in @campminder/components; a JSX prop on it is a silent no-op. useWebComponentProperty(ref, 'tree', value) is the one sanctioned way this package offers to assign it, via useLayoutEffect so it lands before paint. It is generic on purpose — the same footgun applies to cm-top-bar's data.
  • The definite-height requirement (finding 4) is documented, not silently patched — see "Definite height" below — but <CampminderShell> also measures itself after mount and console.warns once if it renders at 0px height, so the failure is loud rather than a page that quietly renders with no chrome visible.
  • The collapse toggle (finding 5) is wired, not inert. cm-side-nav dispatches cm-side-nav-toggle (bubbles, composed) and deliberately does not mutate its own collapsed — the shell is the declared owner. <CampminderShell> listens for it on its own host element and applies the result to the shell's collapsed property, with collapsed / defaultCollapsed / onCollapsedChange for a consumer that wants to control or observe it. (Floor 10 also teaches <campminder-app-shell> itself to own this, with per-portal persistence — this wrapper's own listener means a React consumer is never left with an inert toggle even against an older shell version.)

Definite height

<campminder-app-shell>'s :host { height: 100% } only resolves against a containing block that itself has a definite height. Most Next.js App Router pages do not have one — html and body are unconstrained by default — so the chrome silently collapses to its content height. Give an ancestor a definite height:

html,
body {
  height: 100%;
}

or set one on <CampminderShell> itself (e.g. height: 100dvh on a wrapping element). There is no fix for this from inside the shell's shadow root — it depends on an ancestor chain the shell does not control — which is why the failure is a console warning rather than something this package can silently correct.

shouldRenderSideNav / shouldRenderTopBar / shouldRenderSuperUserBanner

Named to match campminder-navigation's AppLayout, whose booleans this wrapper is where the shell's inverted-attribute polarity gets absorbed: <campminder-app-shell> uses no-side-nav / no-top-bar because an HTML boolean attribute cannot default to true. A React consumer of this package never sees the negative attributes — side nav and top bar default true, the banner defaults false.

Next.js App Router — "use client"

<CampminderShell> and useWebComponentProperty are marked 'use client' in source, but this package's own Vite/Rollup build strips module-level directives from the compiled dist/index.js — a known limitation of building a library with Rollup rather than Next's own toolchain. A consumer must re-declare the boundary in their own file that imports this package:

// app/campminder-shell.client.tsx
'use client'
export {CampminderShell, useWebComponentProperty} from '@campminder/campminder-shell-react'

and import from that file rather than from @campminder/campminder-shell-react directly in a Server Component tree. Tracked as a Floor 10 release-checklist item; not fixed in this package because it costs every consumer more to work around than to declare it once, and the workaround is the standard shape recommended for any third-party React package that ships client-only components.

Not in this package's scope

Following the README's boundary above (this wraps the shell, not the chrome): it does not depend on, import, or re-export anything from @campminder/components. A consumer renders <cm-top-bar> / <cm-super-user-banner> the same way as <cm-side-nav> in the example above — plain custom elements, slotted as children, with useWebComponentProperty for any non-string property.