Creating Custom Admin Panel
Django CFG provides a powerful and elegant way to create custom Next.js admin panels that integrate seamlessly with your Django backend. This guide covers the complete workflow from setup to deployment.
Overview
Django CFG supports dual admin architecture with two independent admin interfaces:
- Tab 1: Built-in Dashboard - Pre-configured admin interface for Django CFG tools
- Tab 2: Custom Next.js Admin - Your custom admin panel (static build, no Node.js server required)
Key Benefits
- ✅ Universal Project - Works as standalone Next.js app AND embedded in Django
- ✅ Zero Server Overhead - Static build doesn’t require Node.js in production
- ✅ Automatic Integration - ZIP archive auto-extracts on first request
- ✅ Type-Safe APIs - Auto-generated TypeScript clients from Django models
- ✅ Hot Reload - Full development experience with instant feedback
Architecture
Quick Start
Step 1: Configure Next.js Admin
In your Django project’s api/config.py:
from django_cfg import DjangoConfig, NextJsAdminConfig
config = DjangoConfig(
# ... other config ...
# === Next.js Admin Integration ===
nextjs_admin: Optional[NextJsAdminConfig] = NextJsAdminConfig(
# Path to Next.js admin project (relative to manage.py directory)
project_path="../frontend/apps/admin",
# Customize where TypeScript clients are copied
api_output_path="app/_lib/api/generated",
# Optional: static files URL prefix (default: /cfg/admin/)
# static_url="/cfg/admin/",
# Optional: Next.js dev server URL (default: http://localhost:3001)
# dev_url="http://localhost:3001",
# Optional: iframe route for default view (default: /private)
# iframe_route="/private",
# Optional: tab title in admin (default: Next.js Admin)
# tab_title="Dashboard",
)
)Step 2: Generate API Clients & Build
In your Next.js admin project directory:
# Generate TypeScript API clients and build Next.js
make apiOr manually:
# From Django project root
cd ../frontend/apps/admin
python ../../../django/manage.py generate_clients --typescript
# This automatically:
# 1. Generates TypeScript clients from Django OpenAPI schema
# 2. Copies clients to app/_lib/api/generated/
# 3. Builds Next.js with NEXT_PUBLIC_STATIC_BUILD=true
# 4. Creates ZIP archive: ../../../django/static/nextjs_admin.zipStep 3: Access in Django Admin
# Start Django server
python manage.py runserver
# Visit: http://localhost:8000/admin/
# You'll see TWO tabs:
# - Tab 1: Built-in Dashboard (Django CFG tools)
# - Tab 2: Your Custom Admin (auto-extracted from ZIP)Complete Workflow
Development Workflow
Production Workflow
Project Structure
Your Next.js admin project should follow this structure:
frontend/apps/admin/
├── app/ # Next.js App Router
│ ├── (pages)/ # Route groups
│ │ ├── admin/ # Admin routes
│ │ ├── private/ # User routes
│ │ └── public/ # Public routes
│ ├── _core/ # Core config
│ │ ├── settings.ts # App settings
│ │ └── metadata.ts # SEO metadata
│ ├── _lib/ # Shared libraries
│ │ └── api/
│ │ └── generated/ # ← Auto-copied TypeScript clients
│ ├── _layouts/ # Layout components
│ └── _routes/ # Route definitions
├── package.json
├── next.config.ts # Static export config
└── tsconfig.jsonConfiguration Details
Next.js Configuration
Your next.config.ts should enable static export:
import { createBaseNextConfig } from '@djangocfg/nextjs/config';
const config = createBaseNextConfig({
isDefaultCfgAdmin: true, // Enables static build mode
});
export default config;Static Build Settings
When NEXT_PUBLIC_STATIC_BUILD=true is set (automatically during build):
output: "export"- Static HTML exportbasePath- Set fromNextJsAdminConfig.static_urlimages.unoptimized: true- Required for static export- All routes pre-rendered as static HTML
API Client Generation
TypeScript clients are automatically generated and copied:
# Generated from Django OpenAPI schema
openapi/clients/typescript/cfg/
├── models.ts # TypeScript models
├── client.ts # API client
└── schemas.ts # Zod schemas
# Auto-copied to Next.js project
frontend/apps/admin/app/_lib/api/generated/cfg/
└── [same structure]Build Process
Automatic Build (Recommended)
When you run make api or python manage.py generate_clients --typescript:
- Generate OpenAPI Schema - Parse Django models/views
- Generate TypeScript Clients - Create type-safe API clients
- Copy to Next.js - Auto-copy to
app/_lib/api/generated/ - Build Next.js - Run
pnpm buildwithNEXT_PUBLIC_STATIC_BUILD=true - Create ZIP Archive - Package
out/directory intonextjs_admin.zip - Copy to Django - Move ZIP to
{BASE_DIR}/static/nextjs_admin.zip
Manual Build
If you prefer manual control:
# 1. Generate API clients
cd ../../../django
python manage.py generate_clients --typescript --no-build
# 2. Build Next.js
cd ../frontend/apps/admin
NEXT_PUBLIC_STATIC_BUILD=true pnpm build
# 3. Create ZIP
cd out
zip -r -q ../../../django/static/nextjs_admin.zip .
# 4. Verify
ls -lh ../../../django/static/nextjs_admin.zip
# Should show ~5-10MB fileZIP Archive & Auto-Extraction
Archive Creation
The ZIP archive is created during build:
# Location: {BASE_DIR}/static/nextjs_admin.zip
# Contents: All files from Next.js out/ directory
# Size: ~5-10MB (compressed from ~20MB uncompressed)Auto-Extraction
On first HTTP request to /cfg/nextjs-admin/:
# Django automatically:
# 1. Detects missing extraction directory
# 2. Extracts ZIP to {BASE_DIR}/static/nextjs_admin/
# 3. Serves files via Django static file serving
# 4. Subsequent requests use extracted files (fast)Performance:
- Extraction time: ~100-200ms (one-time operation)
- Subsequent requests: Instant (served from directory)
- No Node.js server required in production
Dual Admin Interface
Tab 1: Built-in Dashboard
- Purpose: Django CFG tools and utilities
- Source: Pre-built admin from
django_cfgpackage - Path:
/cfg/admin/admin/ - Dev Server:
http://localhost:3777/admin(if running)
Tab 2: Custom Admin
- Purpose: Your custom Next.js admin panel
- Source: Static build from your project
- Path:
/cfg/nextjs-admin/admin/ - Dev Server:
http://localhost:3000/admin(if running) - ZIP:
{BASE_DIR}/static/nextjs_admin.zip
Tab Switching
Users can switch between tabs seamlessly:
<!-- Django Admin Interface -->
<div class="tabs">
<button>Tab 1: Built-in Dashboard</button>
<button>Tab 2: Custom Admin</button>
</div>
<!-- Each tab has its own iframe -->
<iframe id="tab-1" src="/cfg/admin/admin/"></iframe>
<iframe id="tab-2" src="/cfg/nextjs-admin/admin/"></iframe>Development Mode
Running Dev Servers
# Terminal 1: Django
cd projects/django
python manage.py runserver
# Terminal 2: Next.js Admin
cd projects/frontend/apps/admin
pnpm dev # Runs on port 3000
# Terminal 3: Built-in Admin (optional)
cd django-cfg-dev/src/django_admin/apps/admin
pnpm dev # Runs on port 3777Dev Server Detection
Django automatically detects running dev servers:
- Port 3000: Custom Next.js admin (Tab 2)
- Port 3777: Built-in admin (Tab 1)
If dev server is running, Django serves iframe from dev URL. If not, falls back to static files.
Hot Reload
When Next.js dev server is running:
- Changes to React components → Instant hot reload
- Changes to API clients → Restart dev server
- Changes to Django models → Regenerate clients
Production Deployment
Build for Production
# Single command does everything
make api
# Or from Django project:
python manage.py generate_clients --typescriptThis creates:
static/nextjs_admin.zip(~5-10MB)- Ready for deployment
Docker Deployment
# Dockerfile
FROM python:3.11
# Copy Django project (includes static/nextjs_admin.zip)
COPY . /app
WORKDIR /app
# Install dependencies
RUN pip install -r requirements.txt
# Run migrations
RUN python manage.py migrate
# ZIP file is already included, no extraction needed
# Django will auto-extract on first request
# Start server
CMD ["gunicorn", "api.wsgi:application"]First Request Behavior
# First HTTP request to /cfg/nextjs-admin/
# → Django detects missing extraction directory
# → Extracts nextjs_admin.zip to static/nextjs_admin/
# → Serves files
# → Subsequent requests use extracted files (fast)Configuration Options
NextJsAdminConfig Reference
NextJsAdminConfig(
# Required
project_path="../frontend/apps/admin",
# Optional - API Client Path
api_output_path="app/_lib/api/generated", # Default
# Optional - Static Build Output
static_output_path="out", # Default
# Optional - URL Prefix
static_url="/cfg/nextjs-admin/", # Default
# Optional - Dev Server URL
dev_url="http://localhost:3000", # Default
# Optional - iframe Route
iframe_route="/private", # Default
# Optional - Tab Title
tab_title="Custom Admin", # Default: "Next.js Admin"
)Best Practices
1. Universal Project Design
Design your Next.js project to work in both modes:
// app/_core/settings.ts
export const settings = {
// Detects build mode automatically
isStaticBuild: process.env.NEXT_PUBLIC_STATIC_BUILD === 'true',
// API URL: empty for static builds (relative paths)
api: {
baseUrl: process.env.NEXT_PUBLIC_STATIC_BUILD
? '' // Relative paths in static build
: 'http://localhost:8000', // Absolute in dev
},
};2. Route Organization
Organize routes by domain:
app/(pages)/
├── admin/ # Admin routes (/admin/*)
├── private/ # User routes (/private/*)
└── public/ # Public routes (/, /contact, etc.)3. API Client Usage
Use generated clients with type safety:
import { api } from '@/api/generated/cfg';
// Type-safe API calls
const users = await api.cfg_accounts_user_list();
const stats = await api.cfg_tasks_api_tasks_stats_retrieve();4. Static Asset Paths
Use helper functions for static assets:
// app/_core/settings.ts
const getStaticAssetPath = (path: string): string => {
const base = isStaticBuild ? basePath : siteUrl;
return `${base}/static/${path}`;
};
// Usage
settings.app.icons.logo = getStaticAssetPath('logos/logo.svg');Troubleshooting
ZIP Not Found
Error: [nextjs_admin] No ZIP found in solution or package
Solution:
# Build Next.js and create ZIP
cd frontend/apps/admin
make apiOld Files Served
Symptom: HTML shows outdated content
Solution:
# Delete extracted directory (forces re-extraction)
rm -rf {BASE_DIR}/static/nextjs_admin/
# Next request will re-extract from ZIPDev Server Not Detected
Symptom: Tabs use static files despite dev server running
Possible Causes:
- Dev server still compiling (wait 5-10 seconds)
- Wrong port (check
package.jsonscripts) - Firewall blocking connection
Solution:
# Check if dev server is running
lsof -i :3000 | grep LISTEN
# Test connection
curl -I http://localhost:3000/adminBuild Errors
Error: TypeScript errors during build
Solution:
# Check types first
cd frontend/apps/admin
pnpm check
# Fix errors, then rebuild
make apiSummary
Django CFG’s custom admin integration provides:
- ✅ Universal Projects - Same codebase works standalone and embedded
- ✅ Zero Server Overhead - Static build, no Node.js in production
- ✅ Automatic Integration - ZIP auto-extracts, no manual steps
- ✅ Type-Safe APIs - Auto-generated clients from Django models
- ✅ Dual Admin Strategy - Built-in tools + custom React interface
- ✅ Hot Reload - Full development experience
The result: A simple, elegant solution that “just works” - configure once, deploy anywhere, no Node.js server required in production.