@consilioweb/payload-admin-theme
v0.2.1
Published
Payload CMS plugin — customize admin panel appearance with theming, branding, and CSS variables
Downloads
125
Maintainers
Readme
@consilioweb/payload-admin-theme
[!IMPORTANT]
⚠️ Next.js 16 + Turbopack — Known Issue
If you're using Next.js 16 with Turbopack (default bundler), you may encounter a
createContext is not a functionerror duringnext build. This is a known Payload CMS issue (#15429, #14330) — not specific to this plugin.Workaround — Add this to your admin page (
src/app/(payload)/admin/[[...segments]]/page.tsx):export const dynamic = 'force-dynamic'And ensure all
@consilioweb/*packages are intranspilePackagesin yournext.config.ts:transpilePackages: ['@consilioweb/seo-analyzer', '@consilioweb/admin-nav', /* ...other @consilioweb packages */],✅ Next.js 15 works without any workaround.
Payload CMS 3 plugin to customize the admin panel appearance: colors, branding, logo, login page, and custom CSS — all configurable from the admin UI.
Features
- Color theming with built-in color picker fields
- Dark mode — dedicated color fields for
[data-theme="dark"](v0.2.0) - 4 theme presets — Blue Professional, Dark Minimal, Green Nature, Purple Creative (v0.2.0)
- Brand customization — name, logo, favicon
- Login page — custom title, subtitle, and logo
- CSS variable injection — overrides Payload's default theme
- Custom CSS — inject additional styles from the admin panel (with injection validation)
- Hide Payload branding — remove default Payload logo
- Nav link — automatic "Admin Theme" link in the sidebar
- Server-side rendering — no client-side flicker
- AdminThemeContext — single fetch shared across all components (v0.2.0)
- Security — CSS injection validation, hex color validation, URL validation on logo/favicon (v0.2.0)
Installation
pnpm add @consilioweb/payload-admin-theme
# or
npm install @consilioweb/payload-admin-themeQuick Start
import { buildConfig } from 'payload'
import { adminThemePlugin } from '@consilioweb/payload-admin-theme'
export default buildConfig({
plugins: [
adminThemePlugin({
brandName: 'My Company',
primaryColor: '#3B82F6',
accentColor: '#10B981',
}),
],
})That's it! The plugin adds an "Admin Theme" global to your admin panel where you can change all settings live.
Plugin Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| enabled | boolean | true | Enable/disable the plugin |
| globalSlug | string | 'admin-theme' | Slug for the Payload global |
| brandName | string | — | Brand name displayed in admin header |
| primaryColor | string | '#3B82F6' | Primary color (hex) |
| accentColor | string | '#10B981' | Accent color (hex) |
| sidebarColor | string | — | Sidebar background color |
| borderRadius | number | 8 | Border radius for UI elements (px) |
| hidePayloadBranding | boolean | false | Hide default Payload branding |
| faviconUrl | string | — | Custom favicon URL |
| customCSS | string | — | Custom CSS injected into admin |
| addNavLink | boolean | true | Add "Theme" link in admin sidebar |
| navLinkPath | string | (built-in) | Override nav link component path |
| skipComponentInjection | boolean | false | Skip automatic component injection |
| colorPickerComponent | string \| false | (built-in) | Override or disable color picker |
| access | function | Admin-only | Custom access control function for the global |
| presets | boolean | true | Enable the 4 built-in theme presets |
Dark Mode (v0.2.0)
The plugin supports dedicated dark mode colors. In the admin UI, a Dark Mode section lets you configure separate colors for [data-theme="dark"]:
adminThemePlugin({
primaryColor: '#3B82F6', // Light mode primary
accentColor: '#10B981', // Light mode accent
// Dark mode colors are configured from the admin UI
})Theme Presets (v0.2.0)
4 built-in presets can be applied with one click from the admin panel:
| Preset | Primary | Accent | Style |
|--------|---------|--------|-------|
| Blue Professional | #3B82F6 | #10B981 | Clean corporate |
| Dark Minimal | #6366F1 | #F59E0B | Dark background |
| Green Nature | #059669 | #D97706 | Organic feel |
| Purple Creative | #7C3AED | #EC4899 | Bold creative |
Admin UI Configuration
The plugin adds an "Admin Theme" global (under Settings group) with these sections:
General
- Brand name
- Primary, accent, and sidebar colors (with color picker)
- Border radius
- Logo and favicon URLs
Login Page
- Login title (e.g. "Welcome")
- Login subtitle
- Login logo URL (overrides brand name badge)
Advanced
- Hide Payload branding checkbox
- Custom CSS textarea
Changes are applied on the next page load.
CSS Variables
The plugin maps theme colors to Payload CSS variables:
| Payload Variable | Source |
|-----------------|--------|
| --theme-success-500 | primaryColor |
| --theme-text-link | primaryColor |
| --theme-success-400/600 | primaryColor (lighter/darker) |
| --theme-warning-500 | accentColor |
| --theme-warning-400/600 | accentColor (lighter/darker) |
| --nav-color | sidebarColor |
| --style-radius-s/m/l | borderRadius |
Server-Side CSS Endpoint
For projects where client-side injection doesn't work (e.g. symlinked packages in dev), you can use a server-side CSS endpoint:
- Create an API route at
src/app/api/admin-theme-css/route.ts:
import { NextResponse } from 'next/server'
import { getPayload } from 'payload'
import config from '@payload-config'
export async function GET() {
const payload = await getPayload({ config })
const theme = await payload.findGlobal({ slug: 'admin-theme' })
const vars: string[] = []
if (theme.primaryColor) {
vars.push(`--theme-success-500: ${theme.primaryColor}`)
vars.push(`--theme-text-link: ${theme.primaryColor}`)
}
// ... add more variables as needed
const css = vars.length > 0
? `html:root {\n ${vars.join(';\n ')};\n}\n`
: '/* no theme */'
return new NextResponse(css, {
headers: { 'Content-Type': 'text/css; charset=utf-8' },
})
}- Import it in your
custom.scss:
@import url('/api/admin-theme-css');Dynamic Login & Branding Components
The plugin includes server components for dynamic branding. For projects using skipComponentInjection: true, create local server components that read from the admin-theme global:
// src/components/AdminLogo/index.tsx
import { getPayload } from 'payload'
import config from '@payload-config'
const AdminLogo = async () => {
const payload = await getPayload({ config })
const theme = await payload.findGlobal({ slug: 'admin-theme' })
return (
<div style={{ fontWeight: 900, fontSize: 18, color: 'var(--theme-text)' }}>
{(theme.brandName as string)?.toUpperCase() || 'ADMIN'}
</div>
)
}
export default AdminLogoRegister in payload.config.ts:
admin: {
components: {
graphics: {
Logo: '@/components/AdminLogo',
Icon: '@/components/AdminIcon',
},
beforeLogin: ['@/components/BeforeLogin'],
},
}Compatibility
- Payload CMS 3.0+
- Next.js 14 or 15
- React 18 or 19
Roadmap
- Live preview while editing colors (real-time CSS injection in form)
- Export / import theme configuration as JSON
- Custom font selection (Google Fonts integration)
- Per-user theme preferences (dark/light override)
- Color contrast checker (WCAG AA/AAA validation)
- Additional presets (10+ themes)
- Custom CSS syntax highlighting in admin editor
☕ Support
If this plugin saves you time, consider buying me a coffee!
License
MIT - ConsilioWEB
