Shared layout components for Statsbygg microfrontend architecture
npm install @statsbygg/layoutSelf-contained shared layout package for Statsbygg's Next.js microfrontend architecture using DigDir Designsystemet.
This package provides a consistent layout and state management across multiple independent Next.js applications (zones) in a microfrontend architecture.
- 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
``bash`
npm install @statsbygg/layout
This package requires:
- next >= 14.0.0react
- >= 18.0.0react-dom
- >= 18.0.0
``
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
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 (
`
#### 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' }
]
}
]
}
]
};
`
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
`
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 (
Current theme: {theme}
Logged in as {user.name}
}
API Reference
$3
Main layout component that orchestrates the entire layout structure.
`tsx
interface RootLayoutProps {
children: React.ReactNode;
routes: RouteNode;
className?: string;
}
`$3
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;
}
`
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 components:
-
Breadcrumbs (with List, Item, Link)
- Button
- Heading
- Link
- ParagraphAll styling uses Designsystemet design tokens:
-
--ds-spacing-* for spacing
- --ds-color-* for colors
- --ds-font-size-* for typographyDevelopment
`bash
Install dependencies
npm installBuild the package
npm run buildWatch mode for development
npm run devType checking
npm run type-checkLinting
npm run lint
`Dependencies
This package includes:
-
@digdir/designsystemet-react ^1.5.1
- @statsbygg/design-tokens ^0.2.0
- clsx ^2.0.0
- zustand` ^5.0.4Internal use only - Statsbygg