npm package: @statsbygg/layout
Description: Shared layout components for Statsbygg microfrontend architecture
Latest version: 0.2.3

# @statsbygg/layout

Self-contained shared layout package for Statsbygg's Next.js microfrontend architecture using DigDir Designsystemet.

## Overview

This package provides a consistent layout and state management across multiple independent Next.js applications (zones) in a microfrontend architecture.

## Features

- **DigDir Designsystemet Integration**: Uses official Norwegian design system components
- **Self-Contained State Management**: Built-in zustand store for global state (user, theme, locale)
- **Dynamic Breadcrumbs**: Automatic breadcrumb generation with responsive behavior
- **Cross-Zone Synchronization**: State persists across browser tabs and zone navigation
- **CSS Modules with Colocation**: Each component follows ComponentName/ComponentName.tsx pattern
- **TypeScript**: Full type safety with separate .types.ts files
- **Function Declarations**: All functions use function declaration syntax

## Installation

```bash
npm install @statsbygg/layout
```

## Peer Dependencies

This package requires:

- `next` >= 14.0.0
- `react` >= 18.0.0
- `react-dom` >= 18.0.0

## Package Structure

```
src/
├── components/
│   ├── Breadcrumbs/
│   │   ├── Breadcrumbs.tsx
│   │   ├── Breadcrumbs.types.ts
│   │   ├── Breadcrumbs.module.css
│   │   └── index.ts
│   ├── GlobalHeader/
│   │   ├── GlobalHeader.tsx
│   │   ├── GlobalHeader.types.ts
│   │   ├── GlobalHeader.module.css
│   │   └── index.ts
│   ├── GlobalFooter/
│   │   ├── GlobalFooter.tsx
│   │   ├── GlobalFooter.types.ts
│   │   ├── GlobalFooter.module.css
│   │   └── index.ts
│   └── RootLayout/
│       ├── RootLayout.tsx
│       ├── RootLayout.types.ts
│       ├── RootLayout.module.css
│       └── index.ts
├── store/
│   └── globalState.ts
├── utils/
│   └── routeRegistry.ts
└── index.ts
```

## Usage

### 1. Defining Your Route Tree

The routing system uses **URL-Driven Logic**. You define a single `RouteNode` tree passed to `RootLayout`.

- **External Links**: Any path starting with `http` or `https` is treated as an external parent.
- **Internal Links**: Any relative path (e.g., `/`, `/about`) is treated as internal to your application (relative to your `basePath`).

#### Scenario A: Standard Application

_This app sits directly under the main Statsbygg site._

```tsx
// app/layout.tsx
import { RootLayout, RouteNode } from "@statsbygg/layout";

const ROUTES: RouteNode = {
  label: "Hjem",
  path: "https://statsbygg.no", // External Parent
  children: [
    {
      label: "My App",
      path: "/", // App Root (relative to basePath)
      children: [
        { label: "Page 1", path: "/page-1" },
        { label: "Page 2", path: "/page-2" },
      ],
    },
  ],
};

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="no">
      <body>
        <RootLayout routes={ROUTES}>{children}</RootLayout>
      </body>
    </html>
  );
}
```

#### Scenario B: Deeply Nested Microfrontend

_This app sits deep within a hierarchy of other applications._

```tsx
// app/layout.tsx
const ROUTES: RouteNode = {
  label: "Hjem",
  path: "https://statsbygg.no",
  children: [
    {
      label: "Parent Route",
      path: "https://statsbygg.no/parent-route", // External Parent 2
      children: [
        {
          label: "Your App Name",
          path: "/", // Your App Root
          children: [{ label: "Your App Route", path: "/your-app-route" }],
        },
      ],
    },
  ],
};
```

### 2. Handling Dynamic Routes

For pages with dynamic IDs (e.g., `/properties/[id]`), use the `useBreadcrumbs` hook.

**Note:** You only need to define the _dynamic_ portion of the path. The layout package automatically prepends the static parents (Home, App Root, etc.) defined in your `ROUTES` tree.

```tsx
// app/properties/[id]/page.tsx
"use client";
import { useBreadcrumbs } from "@statsbygg/layout";

export default function PropertyPage({ params, propertyName }) {
  useBreadcrumbs([
    { label: "Properties", href: "/properties" },
    { label: propertyName, href: `/properties/${params.id}` },
  ]);

  return <div>...</div>;
}
// Resulting Breadcrumbs: Home > My App > Properties > [Property Name]
```

### Using the Global Store

The package exports a zustand store for managing global state:

```tsx
import { useGlobalStore } from "@statsbygg/layout";

function MyComponent() {
  const { user, theme, setUser, setTheme } = useGlobalStore();

  function handleLogin() {
    setUser({ name: "John Doe", email: "john@statsbygg.no" });
  }

  function toggleTheme() {
    setTheme(theme === "light" ? "dark" : "light");
  }

  return (
    <div>
      <p>Current theme: {theme}</p>
      {user && <p>Logged in as {user.name}</p>}
      <button onClick={toggleTheme}>Toggle theme</button>
    </div>
  );
}
```

## API Reference

### RootLayout

Main layout component that orchestrates the entire layout structure.

```tsx
interface RootLayoutProps {
  children: React.ReactNode;
  routes: RouteNode;
  className?: string;
}
```

### useGlobalStore

Zustand store hook for global state management.

```tsx
interface GlobalState {
  user: { name: string; email: string } | null;
  theme: "light" | "dark";
  locale: "no" | "en";
  setUser: (user: { name: string; email: string } | null) => void;
  setTheme: (theme: "light" | "dark") => void;
  setLocale: (locale: "no" | "en") => void;
  initialize: () => Promise<void>;
}
```

## Breadcrumbs Behavior

The Breadcrumbs component uses Designsystemet's responsive behavior:

- **On narrow screens (<650px)**: Shows a back button to parent level
- **On wide screens (≥650px)**: Shows full breadcrumb path
- **Last item**: Automatically marked with `aria-current="page"`

## State Persistence

Global state is automatically persisted to localStorage using zustand's persist middleware. This enables:

- **Cross-tab synchronization**: Changes in one tab reflect in others
- **Cross-zone persistence**: State is maintained when navigating between zones
- **Session persistence**: State survives page refreshes

## Design System Integration

This package uses [DigDir Designsystemet](https://designsystemet.no) components:

- `Breadcrumbs` (with List, Item, Link)
- `Button`
- `Heading`
- `Link`
- `Paragraph`

All styling uses Designsystemet design tokens:

- `--ds-spacing-*` for spacing
- `--ds-color-*` for colors
- `--ds-font-size-*` for typography

## Development

```bash
# Install dependencies
pnpm install

# Build the package
pnpm build

# Watch mode for development
pnpm dev

# Type checking
pnpm type-check

# Linting
pnpm lint
```

## Dependencies

This package includes:

- `@digdir/designsystemet-react` 1.12.1
- `@statsbygg/design-tokens` ^0.2.0
- `clsx` ^2.0.0
- `zustand` ^5.0.4

## License

Internal use only - Statsbygg