
Research
2025 Report: Destructive Malware in Open Source Packages
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.
@djangocfg/layouts
Advanced tools
Simple, straightforward layout components for Next.js - import and use with props
Simple, straightforward layout components for Next.js - import and use with props.
Part of DjangoCFG — modern Django framework for production-ready SaaS applications.
pnpm add @djangocfg/layouts
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 }}
pushNotifications={{
enabled: true,
vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
}}
mcpChat={{ enabled: true, autoDetectEnvironment: true }}
>
{children}
</BaseApp>
</body>
</html>
);
}
Included providers:
@djangocfg/api)Global components:
Note: Auth functionality is provided by
@djangocfg/apipackage. See @djangocfg/api documentation for auth usage.
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 }}
pushNotifications={{
enabled: true,
vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || ''
}}
// 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.
Simple, props-based layout components. No complex configs needed!
import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
// Public page
<PublicLayout
logo="/logo.svg"
siteName="My App"
navigation={navItems}
>
{children}
</PublicLayout>
// Private page
<PrivateLayout
sidebar={{ 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 |
Extension Layouts: Additional layouts like
SupportLayoutandPaymentsLayoutare available in extension packages:
@djangocfg/ext-support- Support ticket layouts@djangocfg/ext-payments- Payment flow layouts
Google Analytics integration via react-ga4. Auto-tracks pageviews and user sessions.
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').
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');
| 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 |
Built-in tracking for:
@djangocfg/api)Progressive Web App features and Web Push notifications support.
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:
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.
Enable Web Push notifications with pushNotifications config:
import { BaseApp } from '@djangocfg/layouts';
<BaseApp
pushNotifications={{
enabled: true,
vapidPublicKey: process.env.NEXT_PUBLIC_VAPID_KEY || '',
subscribeEndpoint: '/api/push/subscribe', // Backend subscription endpoint
requirePWA: true, // Only show if installed as PWA
autoSubscribe: false, // Auto-subscribe on install
delayMs: 5000, // Delay before showing prompt
resetAfterDays: 7, // Re-show after dismissal
}}
>
{children}
</BaseApp>
Features:
import { usePwa, useDjangoPushContext } from '@djangocfg/layouts/snippets';
// PWA status
const { isPWA, isInstallable } = usePwa();
// Push notifications (Django integration)
const {
isSupported,
isSubscribed,
permission,
pushes,
subscribe,
unsubscribe,
sendPush,
clearPushes,
removePush,
} = useDjangoPushContext();
// Subscribe to push
await subscribe();
// Send test notification (via Django API)
await sendPush({
title: 'Hello!',
body: 'Test notification',
icon: '/icon.png',
});
// Manage push history
clearPushes(); // Clear all
removePush(pushId); // Remove specific push
Generate VAPID keys for push notifications:
# Using web-push CLI
npx web-push generate-vapid-keys
# Or use openssl
openssl ecparam -name prime256v1 -genkey -noout -out vapid_private.pem
openssl ec -in vapid_private.pem -pubout -out vapid_public.pem
Add keys to .env.local:
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_public_key_here
VAPID_PRIVATE_KEY=your_private_key_here
Reusable UI components and hooks ready to use.
import {
Breadcrumbs,
AuthDialog,
usePwa,
useDjangoPushContext,
} 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 |
useDjangoPushContext | Django push notifications hook (subscribe, send, history) |
usePush | Generic push hook (for non-Django apps) |
A2HSHint | Add to Home Screen hint component |
PWAPageResumeManager | Component for PWA page resume (use via BaseApp config) |
PushPrompt | Push notification permission prompt |
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)
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 },
]}
/>
import { AuthDialog, openAuthDialog } from '@djangocfg/layouts/snippets';
// Add to layout
<AuthDialog authPath="/auth" />
// Trigger from anywhere
openAuthDialog({ message: 'Sign in to continue' });
Utility components organized by category.
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 |
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="support@example.com" />;
}
// app/error.tsx
'use client';
import { ErrorLayout } from '@djangocfg/layouts/components/errors';
export default function Error({ error, reset }) {
return <ErrorLayout code={500} supportEmail="support@example.com" />;
}
import { RedirectPage } from '@djangocfg/layouts/components/RedirectPage';
// app/page.tsx
export default function Page() {
return (
<RedirectPage
authenticatedPath="/dashboard"
unauthenticatedPath="/auth"
loadingText="Loading..."
/>
);
}
import {
ErrorTrackingProvider,
useErrors,
ErrorButtons,
ErrorToast
} from '@djangocfg/layouts/components/errors/ErrorsTracker';
<ErrorTrackingProvider>
<YourApp />
</ErrorTrackingProvider>
// In components
const { addError, clearErrors, errors } = useErrors();
import { UpdateNotifier } from '@djangocfg/layouts/components/UpdateNotifier';
<UpdateNotifier />
Ready-to-use page components.
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 |
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 |
| 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
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.
This package follows a simple, props-based approach:
// app/layout.tsx
import { AppLayout } from '@djangocfg/layouts';
import { appLayoutConfig } from './_config/appLayoutConfig';
export default function RootLayout({ children }) {
return (
<AppLayout config={appLayoutConfig}>
{children}
</AppLayout>
);
}
// 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>
);
}
// app/dashboard/page.tsx
import { PrivateLayout } from '@djangocfg/layouts';
export default function DashboardPage() {
return (
<PrivateLayout
sidebar={{
items: [
{ label: 'Dashboard', href: '/dashboard', icon: 'LayoutDashboard' },
{ label: 'Settings', href: '/settings', icon: 'Settings' }
]
}}
header={{
title: 'Dashboard',
userMenu: { name: 'John Doe', email: 'john@example.com' }
}}
>
<h1>Dashboard</h1>
</PrivateLayout>
);
}
// 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:
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 |
// 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:
enable2FASetup={false} in AuthLayout to skip post-login 2FA setup promptenable2FA={true} in ProfileLayout to allow users to manage 2FA from settingsMIT
FAQs
Simple, straightforward layout components for Next.js - import and use with props
The npm package @djangocfg/layouts receives a total of 2,839 weekly downloads. As such, @djangocfg/layouts popularity was classified as popular.
We found that @djangocfg/layouts demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.

Research
/Security News
A five-month operation turned 27 npm packages into durable hosting for browser-run lures that mimic document-sharing portals and Microsoft sign-in, targeting 25 organizations across manufacturing, industrial automation, plastics, and healthcare for credential theft.