@djangocfg/layouts
v2.1.194
Published
Simple, straightforward layout components for Next.js - import and use with props
Downloads
7,583
Maintainers
Readme
@djangocfg/layouts
Simple, straightforward layout components for Next.js - import and use with props.
Part of DjangoCFG — modern Django framework for production-ready SaaS applications.
Install
pnpm add @djangocfg/layoutsCore Components
BaseApp
Core providers wrapper - use when you need providers without layout routing:
import { BaseApp } from '@djangocfg/layouts';
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<BaseApp
theme={{ defaultTheme: 'dark' }}
analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
centrifugo={{ enabled: true, url: process.env.NEXT_PUBLIC_CENTRIFUGO_URL }}
pwaInstall={{ enabled: true, showInstallHint: true }}
mcpChat={{ enabled: true, autoDetectEnvironment: true }}
>
{children}
</BaseApp>
</body>
</html>
);
}Included providers:
- ThemeProvider - Light/dark/system theme management
- TooltipProvider - Tooltip positioning context
- SWRConfig - Data fetching configuration
- AuthProvider - Authentication context (from
@djangocfg/api) - AnalyticsProvider - Google Analytics (optional)
- CentrifugoProvider - WebSocket real-time (optional)
- PwaProvider - PWA installation (optional)
- ErrorTrackingProvider - Error handling and tracking
- ErrorBoundary - React error boundary
- MCP Chat Widget - AI chat assistant (optional)
Global components:
- PageProgress - NProgress bar for route changes
- Toaster - Toast notifications container
- A2HSHint - PWA install hint (if enabled)
Note: Auth functionality is provided by
@djangocfg/apipackage. See @djangocfg/api documentation for auth usage.
AppLayout
Smart layout router built on BaseApp - automatically selects layout based on route:
import { AppLayout } from '@djangocfg/layouts';
import { PublicLayout } from './_layouts/PublicLayout';
import { PrivateLayout } from './_layouts/PrivateLayout';
import { AdminLayout } from './_layouts/AdminLayout';
// app/layout.tsx
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<AppLayout
// Provider configs (passed to BaseApp)
theme={{ defaultTheme: 'system' }}
analytics={{ googleTrackingId: 'G-XXXXXXXXXX' }}
pwaInstall={{ enabled: true }}
// Layout components
publicLayout={{
component: PublicLayout,
enabledPath: ['/', '/legal', '/contact']
}}
privateLayout={{
component: PrivateLayout,
enabledPath: ['/dashboard', '/profile']
}}
adminLayout={{
component: AdminLayout,
enabledPath: '/admin'
}}
// Skip layout for fullscreen pages (providers still applied)
noLayoutPaths={['/private/terminal', '/embed']}
>
{children}
</AppLayout>
</body>
</html>
);
}Layout priority: Admin → Private → Public → Fallback
noLayoutPaths: Paths that render without any layout wrapper. Useful for fullscreen pages (terminal, embed, print). Providers (auth, theme, centrifugo) are still applied.
Layouts
Simple, props-based layout components. No complex configs needed!
Available Layouts
import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
// Public page
<PublicLayout
logo="/logo.svg"
siteName="My App"
navigation={navItems}
>
{children}
</PublicLayout>
// Private page
<PrivateLayout
sidebar={{
groups: [
{ label: 'Main', items: menuItems }
]
}}
header={{ title: 'Dashboard' }}
>
{children}
</PrivateLayout>
// Auth page
<AuthLayout
logo="/logo.svg"
title="Sign In"
subtitle="Welcome back"
>
<LoginForm />
</AuthLayout>| Layout | Description |
|--------|-------------|
| BaseApp | Core providers wrapper (Theme, Auth, SWR, ErrorTracking, Toaster) |
| AppLayout | Smart layout router with route-based layout switching |
| PublicLayout | Public pages (home, docs, contact, legal) |
| PrivateLayout | Authenticated user pages (dashboard, profile) |
| AuthLayout | Authentication pages (login, signup, password reset) |
| AdminLayout | Admin panel layout |
| ProfileLayout | User profile pages |
i18n Support
Pass i18n config to AppLayout for locale switching in all layouts:
import { AppLayout } from '@djangocfg/layouts';
import { useLocaleSwitcher } from '@djangocfg/nextjs/i18n/client';
function RootLayout({ children }) {
const { locale, locales, changeLocale } = useLocaleSwitcher();
return (
<AppLayout
// ... other configs
i18n={{
locale,
locales,
onLocaleChange: changeLocale,
}}
>
{children}
</AppLayout>
);
}The LocaleSwitcher component will automatically appear in PublicLayout and PrivateLayout headers.
LocaleSwitcher Component
A presentational locale switcher component that can be used standalone:
import { LocaleSwitcher } from '@djangocfg/layouts';
// Basic usage (pass locale data via props)
<LocaleSwitcher
locale="en"
locales={['en', 'ru', 'ko']}
onChange={(locale) => router.push(`/${locale}`)}
/>
// With custom labels and styling
<LocaleSwitcher
locale={currentLocale}
locales={['en', 'ru', 'ko']}
onChange={handleLocaleChange}
labels={{ en: 'English', ru: 'Русский', ko: '한국어' }}
variant="outline"
size="sm"
showIcon={true}
/>Props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| locale | string | - | Current locale code |
| locales | string[] | - | Available locale codes |
| onChange | (locale: string) => void | - | Callback when locale changes |
| labels | Record<string, string> | Built-in | Custom labels for locales |
| showCode | boolean | false | Show locale code with label |
| variant | 'ghost' \| 'outline' \| 'default' | 'ghost' | Button variant |
| size | 'sm' \| 'default' \| 'lg' \| 'icon' | 'sm' | Button size |
| showIcon | boolean | true | Show globe icon |
| className | string | - | Custom CSS class |
Smart version: For automatic locale detection with next-intl hooks, use
@djangocfg/nextjs/i18n/componentswhich wraps this component.
Extension Layouts: Additional layouts like
SupportLayoutandPaymentsLayoutare available in extension packages:
@djangocfg/ext-support- Support ticket layouts@djangocfg/ext-payments- Payment flow layouts
Analytics
Google Analytics integration via react-ga4. Auto-tracks pageviews and user sessions.
Setup
Add tracking ID to your config:
// appLayoutConfig.ts
export const appLayoutConfig: AppLayoutConfig = {
// ...
analytics: {
googleTrackingId: 'G-XXXXXXXXXX',
},
};Analytics is automatically initialized by AppLayout. Works only in production (NODE_ENV === 'production').
Usage
import { useAnalytics, Analytics, AnalyticsEvent, AnalyticsCategory } from '@djangocfg/layouts';
// In React components - auto-tracks pageviews
const { event, isEnabled } = useAnalytics();
event(AnalyticsEvent.THEME_CHANGE, {
category: AnalyticsCategory.ENGAGEMENT,
label: 'dark',
});
// Outside React (utilities, handlers)
Analytics.event('button_click', { category: 'engagement', label: 'signup' });
Analytics.setUser('user-123');Predefined Events
| Category | Events |
|----------|--------|
| Auth | AUTH_LOGIN_SUCCESS, AUTH_LOGOUT, AUTH_SESSION_EXPIRED, AUTH_TOKEN_REFRESH |
| OAuth | AUTH_OAUTH_START, AUTH_OAUTH_SUCCESS, AUTH_OAUTH_FAIL |
| Error | ERROR_BOUNDARY, ERROR_API, ERROR_VALIDATION, ERROR_NETWORK |
| Navigation | NAV_ADMIN_ENTER, NAV_DASHBOARD_ENTER, NAV_PAGE_VIEW |
| Engagement | THEME_CHANGE, SIDEBAR_TOGGLE, MOBILE_MENU_OPEN |
Auto-tracking
Built-in tracking for:
- Page views - on every route change
- User ID - automatically set when user is authenticated
- Auth events - login, logout, session expiry (from
@djangocfg/api) - OAuth events - GitHub OAuth start, success, failure
- Errors - React ErrorBoundary errors
PWA Installation
Enable PWA installation prompts with pwaInstall config:
import { BaseApp } from '@djangocfg/layouts';
<BaseApp
pwaInstall={{
enabled: true,
showInstallHint: true, // Show A2HS hint
resetAfterDays: 3, // Re-show after dismissal
delayMs: 1000, // Delay before showing
logo: '/logo192.png', // PWA logo
resumeLastPage: true, // Resume last page on PWA launch
}}
>
{children}
</BaseApp>Features:
- A2HSHint - Platform-specific install hints (iOS Safari/Chrome/Firefox, Android Chrome, Desktop)
- Page Resume - Automatically navigate to last viewed page when PWA is launched
- Auto-detection - Detects if running as PWA
- Dismissal tracking - Respects user dismissal with localStorage
- Custom timing - Configurable delay and reset periods
Page Resume:
When resumeLastPage: true, the app saves the current pathname on every navigation and restores it when the PWA is launched. Pages like /auth, /login, /error are automatically excluded. Data expires after 24 hours.
Usage
import { usePwa } from '@djangocfg/layouts/snippets';
// PWA status
const { isPWA, isInstallable } = usePwa();Snippets
Reusable UI components and hooks ready to use.
import {
Breadcrumbs,
AuthDialog,
usePwa,
} from '@djangocfg/layouts/snippets';| Snippet | Description |
|---------|-------------|
| Breadcrumbs | Navigation breadcrumbs with automatic path generation |
| AuthDialog | Auth modal (login/register) with event-based triggers |
| AnalyticsProvider | Analytics wrapper component |
| usePwa | PWA status hook (isPWA, isInstallable, etc.) |
| usePWAPageResume | Resume last page on PWA launch |
| A2HSHint | Add to Home Screen hint component |
| PWAPageResumeManager | Component for PWA page resume (use via BaseApp config) |
Extension Snippets: Additional components are available in extension packages:
@djangocfg/ext-leads- ContactForm, ContactPage, ContactInfo@djangocfg/ext-knowbase- KnowledgeChat, ChatWidget, ChatUIProvider@djangocfg/ext-newsletter- Hero (with newsletter subscription)
Breadcrumbs
import Breadcrumbs from '@djangocfg/layouts/snippets';
// Auto-generate from current path
<Breadcrumbs />
// Or provide custom items
<Breadcrumbs
items={[
{ path: '/', label: 'Home', isActive: false },
{ path: '/products', label: 'Products', isActive: true },
]}
/>AuthDialog
import { AuthDialog, openAuthDialog } from '@djangocfg/layouts/snippets';
// Add to layout
<AuthDialog authPath="/auth" />
// Trigger from anywhere
openAuthDialog({ message: 'Sign in to continue' });Components
Utility components organized by category.
Core Components
import {
JsonLd,
LucideIcon,
PageProgress,
Suspense
} from '@djangocfg/layouts/components/core';| Component | Description |
|-----------|-------------|
| JsonLd | JSON-LD structured data component |
| LucideIcon | Lucide icon wrapper component |
| PageProgress | Page loading progress indicator |
| Suspense | Suspense wrapper component |
Error Components
import {
ErrorBoundary,
ErrorLayout,
getErrorContent,
ERROR_CODES
} from '@djangocfg/layouts/components/errors';| Component | Description |
|-----------|-------------|
| ErrorBoundary | React error boundary component |
| ErrorLayout | Reusable error page layout (404, 500, etc.) |
| getErrorContent | Get error content by status code |
| ERROR_CODES | Common HTTP error code constants |
ErrorLayout Usage:
// app/not-found.tsx
import { ErrorLayout } from '@djangocfg/layouts/components/errors';
export default function NotFound() {
return <ErrorLayout code={404} supportEmail="[email protected]" />;
}
// app/error.tsx
'use client';
import { ErrorLayout } from '@djangocfg/layouts/components/errors';
export default function Error({ error, reset }) {
return <ErrorLayout code={500} supportEmail="[email protected]" />;
}Redirect Component
import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';
// app/page.tsx
export default function Page() {
return (
<RedirectPage
authenticatedPath="/dashboard"
unauthenticatedPath="/auth"
loadingText="Loading..."
/>
);
}Error Tracking
import {
ErrorTrackingProvider,
useErrors,
useErrorEmitter,
emitRuntimeError,
} from '@djangocfg/layouts';
// Wrap your app
<ErrorTrackingProvider>
<YourApp />
</ErrorTrackingProvider>
// In React components - use hook
function MyComponent() {
const { emitError } = useErrorEmitter('MyComponent');
try {
doSomething();
} catch (error) {
emitError('Something failed', error);
}
}
// Outside React (utilities, libraries) - use standalone function
import { emitRuntimeError } from '@djangocfg/layouts';
try {
doSomething();
} catch (error) {
emitRuntimeError('MyUtility', 'Something failed', error);
}
// Access errors
const { errors, clearErrors } = useErrors();Update Notifier
import { UpdateNotifier } from '@djangocfg/layouts/components/UpdateNotifier';
<UpdateNotifier />Pages
Ready-to-use page components.
Legal Pages
Pre-built legal page components with default configurations.
import {
PrivacyPage,
TermsPage,
CookiesPage,
SecurityPage
} from '@djangocfg/layouts/pages/legal';
// app/legal/privacy/page.tsx
export default PrivacyPage;
// Or customize
import { PrivacyPage, privacyConfig } from '@djangocfg/layouts/pages/legal';
export default function CustomPrivacy() {
return <PrivacyPage config={{
...privacyConfig,
lastUpdated: '2024-01-01',
}} />;
}| Page | Description |
|------|-------------|
| PrivacyPage | Privacy policy page |
| TermsPage | Terms of service page |
| CookiesPage | Cookie policy page |
| SecurityPage | Security policy page |
Utils
Utility functions and helpers.
import {
generateOgImageUrl,
getAbsoluteOgImageUrl,
createOgImageUrlBuilder
} from '@djangocfg/layouts/utils/og-image';
// Generate OG image URL
const ogUrl = generateOgImageUrl('/api/og', {
title: 'My Page',
description: 'Page description',
siteName: 'My Site',
});| Utility | Description |
|---------|-------------|
| generateOgImageUrl | Generate OG image URL with base64 encoding |
| getAbsoluteOgImageUrl | Get absolute OG image URL |
| createOgImageUrlBuilder | Create OG image URL builder with defaults |
Exports
| Path | Content |
|------|---------|
| @djangocfg/layouts | Main exports (all modules) |
| @djangocfg/layouts/layouts | Layout components |
| @djangocfg/layouts/snippets | Reusable components + Analytics |
| @djangocfg/layouts/components | All utility components |
| @djangocfg/layouts/pages | Page components (legal pages) |
| @djangocfg/layouts/pages/legal | Legal page components |
| @djangocfg/layouts/utils | Utilities (og-image, logger) |
| @djangocfg/layouts/styles | CSS |
| @djangocfg/layouts/styles/dashboard | Dashboard-specific CSS |
Auth Exports: For authentication, use
@djangocfg/api/auth- See @djangocfg/api documentation
Extension Packages
Additional functionality is available in extension packages:
| Extension | Package | Description |
|-----------|---------|-------------|
| Newsletter | @djangocfg/ext-newsletter | Newsletter subscription and campaigns |
| Knowledge Base | @djangocfg/ext-knowbase | Documentation, chat, RAG-powered AI |
| Leads | @djangocfg/ext-leads | Lead capture and contact forms |
| Payments | @djangocfg/ext-payments | Payment processing and subscriptions |
| Support | @djangocfg/ext-support | Support tickets and helpdesk |
Each extension has its own layouts, contexts, and components. See individual extension documentation for details.
Requirements
- Next.js >= 15
- React >= 19
- Tailwind CSS >= 4
- react-ga4 (bundled)
- @djangocfg/ui-nextjs (peer dependency)
- @djangocfg/api (peer dependency)
Philosophy
This package follows a simple, props-based approach:
- ✅ No complex configs - just pass props
- ✅ Type-safe - full TypeScript support
- ✅ Flexible - compose layouts as needed
- ✅ Production-ready - built for real apps
- ✅ Modular - core layouts in one package, extensions separate
Examples
Complete App Setup
// app/layout.tsx
import { AppLayout } from '@djangocfg/layouts';
import { appLayoutConfig } from './_config/appLayoutConfig';
export default function RootLayout({ children }) {
return (
<AppLayout config={appLayoutConfig}>
{children}
</AppLayout>
);
}Public Page
// app/page.tsx
import { PublicLayout } from '@djangocfg/layouts';
export default function HomePage() {
return (
<PublicLayout
logo="/logo.svg"
siteName="My App"
navigation={[
{ label: 'Home', href: '/' },
{ label: 'Docs', href: '/docs' },
{ label: 'Contact', href: '/contact' }
]}
>
<h1>Welcome</h1>
</PublicLayout>
);
}Private Dashboard
// app/dashboard/page.tsx
import { PrivateLayout } from '@djangocfg/layouts';
export default function DashboardPage() {
return (
<PrivateLayout
sidebar={{
groups: [
{
label: 'Main',
items: [
{ label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
{ label: 'Settings', href: '/settings', icon: 'Settings' }
]
}
],
homeHref: '/dashboard'
}}
header={{ title: 'Dashboard' }}
>
<h1>Dashboard</h1>
</PrivateLayout>
);
}Auth Page (OTP + 2FA Authentication)
// app/auth/page.tsx
'use client';
import { AuthLayout } from '@djangocfg/layouts';
export default function AuthPage() {
return (
<AuthLayout
sourceUrl="https://example.com"
supportUrl="/support"
termsUrl="/terms"
privacyUrl="/privacy"
enablePhoneAuth={false}
enableGithubAuth={true}
logoUrl="/logo.svg"
redirectUrl="/dashboard"
onOTPSuccess={() => {
console.log('Authentication successful');
}}
onOAuthSuccess={(user, isNewUser, provider) => {
console.log('OAuth success:', { user, isNewUser, provider });
}}
>
<div className="text-center mb-6">
<h2 className="text-2xl font-bold">Welcome to My App</h2>
<p className="text-muted-foreground mt-2">
Sign in with your email or phone
</p>
</div>
</AuthLayout>
);
}Authentication Flow:
- Identifier → Enter email/phone or click GitHub OAuth
- OTP → Enter 6-digit verification code
- 2FA → Enter TOTP code (if 2FA enabled for user)
- Success → Show logo animation, then redirect
AuthLayout Props:
| Prop | Type | Description |
|------|------|-------------|
| sourceUrl | string | Application URL for OTP emails |
| redirectUrl | string | URL to redirect after successful auth (default: /dashboard) |
| logoUrl | string | Logo URL for success screen (SVG recommended) |
| enablePhoneAuth | boolean | Enable phone number authentication |
| enableGithubAuth | boolean | Enable GitHub OAuth |
| enable2FASetup | boolean | Enable 2FA setup prompt after login (default: true). Set to false to skip 2FA setup prompt - users can then configure 2FA from ProfileLayout instead. |
| termsUrl | string | Terms of service URL (shows checkbox if provided) |
| privacyUrl | string | Privacy policy URL |
| supportUrl | string | Support page URL |
| onOTPSuccess | () => void | Callback after successful authentication |
| onOAuthSuccess | (user, isNewUser, provider) => void | Callback after successful OAuth |
| onError | (message: string) => void | Error callback |
Profile Page (with 2FA Management)
// app/profile/page.tsx
'use client';
import { ProfileLayout } from '@djangocfg/layouts';
export default function ProfilePage() {
return (
<ProfileLayout
title="Profile Settings"
description="Manage your account"
enable2FA={true} // Show 2FA management section
showMemberSince={true}
showLastLogin={true}
onUnauthenticated={() => {
// Redirect to login
}}
/>
);
}ProfileLayout Props:
| Prop | Type | Description |
|------|------|-------------|
| title | string | Page title (default: "Profile Settings") |
| description | string | Page description |
| enable2FA | boolean | Show 2FA management section (default: false). When enabled, users can enable/disable Two-Factor Authentication from their profile. |
| showMemberSince | boolean | Show member since date (default: true) |
| showLastLogin | boolean | Show last login date (default: true) |
| onUnauthenticated | () => void | Callback when user is not authenticated |
2FA Configuration Strategy:
- Use
enable2FASetup={false}in AuthLayout to skip post-login 2FA setup prompt - Use
enable2FA={true}in ProfileLayout to allow users to manage 2FA from settings - This gives users control over when to enable 2FA instead of forcing it during login
License
MIT
