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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@logickernel/frame

v0.8.2

Published

Shared navigation and layout components for microfrontends

Downloads

904

Readme

@logickernel/frame

Shared navigation and layout components for microfrontends and core applications.

Overview

This library provides reusable navigation and layout components that can be shared across multiple microfrontends. The library supports both props-based and API-based navigation configuration with role-based filtering.

Features

  • Shared Layout Components: AppSidebar, NavMain, NavUser, TeamSwitcher
  • Dual Navigation Modes:
    • Props-based: Pass navigation items as props
    • API-based: Fetch navigation from API with role-based filtering
  • Role-Based Filtering: Server-side filtering of navigation items based on user roles
  • Type-Safe: Full TypeScript support with exported types
  • Framework Agnostic: Works with any React framework (Next.js, Remix, etc.)

Installation

npm install @logickernel/frame
# or
yarn add @logickernel/frame
# or
pnpm add @logickernel/frame

Peer Dependencies

This library requires the following peer dependencies to be installed in your project:

npm install react react-dom lucide-react
npm install @radix-ui/react-avatar @radix-ui/react-collapsible @radix-ui/react-dropdown-menu @radix-ui/react-slot @radix-ui/react-tooltip

For Next.js users, you'll also need:

npm install next next-auth

UI Components Requirement

This library uses shadcn/ui components that must be available in your application. You need to have the following components installed and accessible via the @/components/ui/* path alias:

  • @/components/ui/avatar
  • @/components/ui/dropdown-menu
  • @/components/ui/collapsible

Important: Do NOT install the sidebar component from shadcn/ui. The library provides all sidebar components (SidebarProvider, Sidebar, SidebarTrigger, SidebarInset, etc.) that share the same React Context. Import them from @logickernel/frame instead.

To install the required components, use shadcn/ui:

npx shadcn@latest add avatar dropdown-menu collapsible

Make sure your tsconfig.json has the path alias configured:

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    }
  }
}

Usage

Framework Adapter

The library is framework-agnostic and requires a framework adapter to be provided. For Next.js applications, use the provided adapter hook:

"use client" // Required: adapter hook must be used in a Client Component

import { AppLayout, useNextJSAdapter } from "@logickernel/frame"
import type { User, SidebarData } from "@logickernel/frame"

export default function Layout({ children }: { children: React.ReactNode }) {
  // Create the adapter using the hook (must be called in a Client Component)
  const adapter = useNextJSAdapter()
  
  return (
    <AppLayout adapter={adapter} user={user} data={sidebarData}>
      {children}
    </AppLayout>
  )
}

Important:

  • The adapter prop is now required (previously optional)
  • useNextJSAdapter() must be called in a Client Component (add "use client" directive)
  • For other frameworks, you'll need to implement the FrameworkAdapter interface (see Types section)

Props Mode vs API Mode

The component automatically detects which mode to use:

  • Props Mode: Provide data → organizations and navigation come from props
  • API Mode: Omit data → organizations and navigation come from the API

Props Mode

Pass a data object containing organizations and navigation items. The {organizationId} placeholder in URLs is automatically replaced.

"use client" // Required: adapter hook must be used in a Client Component

import { AppLayout, useNextJSAdapter } from "@logickernel/frame"
import { Home, Users, GalleryVerticalEnd } from "lucide-react"
import type { SidebarData, User } from "@logickernel/frame"

const user: User = {
  name: "John Doe",
  email: "[email protected]",
  image: null,
}

const data: SidebarData = {
  organizations: [
    { id: "org-1", name: "Acme Corp", plan: "Pro" },
    { id: "org-2", name: "Another Org", plan: "Free" },
  ],
  navigationItems: [
    { title: "Organization", icon: GalleryVerticalEnd },
    { title: "Home", url: "/app/{organizationId}/home", icon: Home },
    { title: "Members", url: "/app/{organizationId}/members", icon: Users },
  ],
}

export default function Layout({ children }: { children: React.ReactNode }) {
  const adapter = useNextJSAdapter()
  
  return (
    <AppLayout adapter={adapter} user={user} data={data}>
      {children}
    </AppLayout>
  )
}

Using SidebarProvider and Sidebar Components

Important: Always import sidebar components (SidebarProvider, SidebarTrigger, SidebarInset, etc.) from @logickernel/frame, not from your local @/components/ui/sidebar. This ensures all components share the same React Context.

"use client" // Required: adapter hook must be used in a Client Component

import {
  AppLayout,
  useNextJSAdapter,
} from "@logickernel/frame"
import type { User, SidebarData } from "@logickernel/frame"

const user: User = { name: "John Doe", email: "[email protected]", image: null }
const data: SidebarData = { /* ... */ }

export default function Layout({ children }: { children: React.ReactNode }) {
  const adapter = useNextJSAdapter()
  
  return (
    <AppLayout adapter={adapter} user={user} data={data}>
      {children}
    </AppLayout>
  )
}

Available Sidebar Components:

  • SidebarProvider - Context provider (required wrapper)
  • SidebarTrigger - Button to toggle sidebar
  • SidebarInset - Main content area that adjusts for sidebar
  • useSidebar - Hook for accessing sidebar state (optional, for advanced use cases)

Note: Other sidebar components (Sidebar, SidebarRail, SidebarMenu, etc.) are internal implementation details used by AppSidebar and are not exported. You don't need to import them.

API Mode

Omit data — the component fetches organizations and navigation from the API.

"use client" // Required: adapter hook must be used in a Client Component

import { AppLayout, useNextJSAdapter } from "@logickernel/frame"

export default function Layout({ children }: { children: React.ReactNode }) {
  const adapter = useNextJSAdapter()
  return (
    <>
      <AppSidebar
        user={user}
        adapter={adapter}
        // apiBaseUrl="/api" // Optional, defaults to "/api"
      />
      <main>{children}</main>
    </>
  )
}

The API endpoint (/api/navigation/[organization_id]) should:

  • Authenticate the user via shared cookies
  • Fetch user roles in the organization
  • Filter navigation items based on role requirements
  • Return filtered navigation items and optionally organizations

Expected API Response:

{
  "items": [
    {
      "title": "Organization",
      "icon": "GalleryVerticalEnd"
    },
    {
      "title": "Home",
      "url": "/app/{organizationId}/home",
      "icon": "Home"
    }
  ],
  "organizationId": "org-1",
  "organizations": [
    {
      "id": "org-1",
      "name": "Acme Corp",
      "plan": "Pro"
    },
    {
      "id": "org-2",
      "name": "Another Org",
      "plan": "Free"
    }
  ]
}

Note: In API mode, the organizations field is required in the API response. The API determines which organizations the user belongs to. The logo field in organizations is optional - if omitted, a default icon will be used.

Components

AppSidebar

Main sidebar component that combines TeamSwitcher, NavMain, and NavUser.

Props:

  • user: User - User information (name, email, image)
  • adapter: FrameworkAdapter - Framework adapter (use createNextJSAdapter() for Next.js)
  • data?: SidebarData - Sidebar data with organizations and navigation items (props mode)
  • organizationId?: string - Current organization ID (optional, extracted from pathname)
  • apiBaseUrl?: string - Custom API base URL (API mode only, defaults to "/api")
  • All other props from Sidebar component

Mode Detection:

  • If data is provided → Props Mode
  • If data is omitted → API Mode

NavMain

Renders navigation menu items with support for groups and nested items.

Props:

  • items: NavigationItem[] - Array of navigation items
  • adapter: FrameworkAdapter - Framework adapter

NavUser

User profile dropdown with sign-out functionality.

Props:

  • user: User - User information
  • adapter: FrameworkAdapter - Framework adapter

TeamSwitcher

Organization switcher dropdown.

Props:

  • teams: Organization[] - List of organizations
  • organizationId?: string - Current organization ID
  • adapter: FrameworkAdapter - Framework adapter

Hooks

useNavigation

Hook to fetch navigation items and organizations from API.

import { useNavigation } from "@logickernel/frame"

const { items, organizations, loading, error } = useNavigation({
  organizationId: "org-1",
  apiBaseUrl: "/api",
  enabled: true,
})

Options:

  • organizationId?: string - Organization ID to fetch navigation for
  • apiBaseUrl?: string - API base URL (default: "/api")
  • enabled?: boolean - Enable/disable fetching (default: true). Useful for conditional fetching, e.g., enabled: !!user to only fetch when a user is logged in

Returns:

  • items: NavigationItem[] - Navigation items
  • organizations: Organization[] - Organizations (if provided by API)
  • loading: boolean - Loading state
  • error: Error | null - Error state

Types

NavigationItem

interface NavigationItem {
  title: string
  url?: string
  icon?: string | LucideIcon
  isActive?: boolean
  items?: {
    title: string
    url: string
  }[]
}

User

interface User {
  name: string | null
  email: string
  image: string | null
}

Organization

interface Organization {
  id: string
  name: string
  logo?: LucideIcon | React.ComponentType<{ className?: string }>
  plan?: string
}

SidebarData

interface SidebarData {
  organizations: Organization[]
  navigationItems: NavigationItem[]
}

NavigationConfig

interface NavigationConfig {
  items: NavigationItem[]
  organizationId?: string
}

FrameworkAdapter

interface FrameworkAdapter {
  usePathname: () => string
  useRouter: () => Router
  Link: ComponentType<LinkProps>
  signOut?: (options?: { redirect?: boolean }) => Promise<void>
}

Router

interface Router {
  push: (path: string) => void
  refresh?: () => void
}

LinkProps

interface LinkProps {
  href: string
  children: ReactNode
  className?: string
}

Utilities

getIconComponent

Utility to convert icon name strings to Lucide React icon components.

import { getIconComponent } from "@logickernel/frame"

const Icon = getIconComponent("Home") // Returns Home icon component

Adapters

createNextJSAdapter

Creates a Next.js adapter for the framework. Use this when using the library in a Next.js application.

import { createNextJSAdapter } from "@logickernel/frame"

const adapter = createNextJSAdapter()

Note: This requires next and next-auth to be installed.

Custom Adapters

For other frameworks, implement the FrameworkAdapter interface:

import type { FrameworkAdapter } from "@logickernel/frame"

const myAdapter: FrameworkAdapter = {
  usePathname: () => {
    // Return current pathname
    return window.location.pathname
  },
  useRouter: () => ({
    push: (path: string) => {
      // Navigate to path
      window.history.pushState({}, "", path)
    },
    refresh: () => {
      // Refresh the page
      window.location.reload()
    },
  }),
  Link: ({ href, children, className }) => (
    <a href={href} className={className}>
      {children}
    </a>
  ),
  signOut: async () => {
    // Handle sign out
  },
}

Role-Based Filtering

Navigation items can specify requiredRoles in the service layer configuration. Items are filtered server-side based on the user's roles in the organization.

Example API-side configuration:

{
  title: "Settings",
  url: "/app/{organizationId}/settings",
  icon: "Settings",
  requiredRoles: ["organization.owner", "organization.editor"]
}

Cross-Origin Setup (Microfrontends)

If your microfrontends are on different domains, configure CORS in your navigation API:

// In your navigation API route
const response = NextResponse.json(data)
response.headers.set("Access-Control-Allow-Origin", "https://microfrontend.example.com")
response.headers.set("Access-Control-Allow-Credentials", "true")
return response

For cross-origin requests, use the full API URL:

<AppSidebar
  user={user}
  organizationId={orgId}
  apiBaseUrl="https://kernel.example.com/api"
/>

Development

# Build the library
npm run build

# Watch mode for development
npm run dev

# Type check
npm run type-check

License

MIT