Custom Views Development in KeystoneJS
KeystoneJS Admin UI is built on React and allows completely replacing or extending interface via custom Views. Not just theming — you can redefine components for specific Lists, add custom pages, change navigation and dashboard.
Types of View Extensions
1. Custom Pages — fully custom pages in Admin UI on arbitrary paths.
2. Custom Navigation — replacing standard menu.
3. List Views — redefining components for specific List.
4. Field Views — field components (covered in Custom Fields section).
Custom Navigation
// keystone.ts
ui: {
getAdditionalFiles: async () => [
{
mode: 'write',
src: `
export { default } from '../admin/navigation';
`,
outputPath: 'admin/config.js',
},
],
},
// admin/navigation.tsx
import { NavigationContainer, NavItem, ListNavItems } from '@keystone-6/core/admin-ui/components';
import type { NavigationProps } from '@keystone-6/core/admin-ui/components';
export default function CustomNavigation({ authenticatedItem, lists }: NavigationProps) {
return (
<NavigationContainer authenticatedItem={authenticatedItem}>
<NavItem href="/">Dashboard</NavItem>
{/* Grouping by sections */}
<div className="mt-4 mb-1 px-3 text-xs font-semibold text-gray-400 uppercase">
Content
</div>
<ListNavItems lists={lists} include={['Post', 'Category', 'Tag']} />
<div className="mt-4 mb-1 px-3 text-xs font-semibold text-gray-400 uppercase">
Users
</div>
<ListNavItems lists={lists} include={['User', 'Role']} />
<NavItem href="/admin/analytics">Analytics</NavItem>
</NavigationContainer>
);
}
Custom Dashboard
// admin/pages/index.tsx — overrides standard dashboard
import { PageContainer } from '@keystone-6/core/admin-ui/components';
import { useQuery, gql } from '@keystone-6/core/admin-ui/apollo';
const STATS_QUERY = gql`
query AdminStats {
postsCount(where: { status: { equals: "published" } })
draftsCount: postsCount(where: { status: { equals: "draft" } })
usersCount
}
`;
export default function CustomDashboard() {
const { data, loading } = useQuery(STATS_QUERY);
return (
<PageContainer header="Control Panel">
<div className="grid grid-cols-3 gap-4 mb-8">
<StatCard label="Published posts" value={data?.postsCount} loading={loading} />
<StatCard label="Drafts" value={data?.draftsCount} loading={loading} />
<StatCard label="Users" value={data?.usersCount} loading={loading} />
</div>
<RecentPostsTable />
</PageContainer>
);
}
Custom Pages with Arbitrary Routes
// admin/pages/analytics.tsx
import { PageContainer } from '@keystone-6/core/admin-ui/components';
export default function AnalyticsPage() {
return (
<PageContainer header="Analytics">
{/* Embed Recharts, Chart.js or Metabase iframe */}
<SalesChart />
<TopContentTable />
</PageContainer>
);
}
Route becomes available at /admin/analytics automatically — Next.js file routing works in Admin UI.
Overriding Item View for Specific List
Completely replace item edit page:
// In List config:
ui: {
itemView: {
// Custom component via file path
// KeystoneJS experimental feature
defaultFieldMode: 'read', // 'hidden' | 'read' | 'edit'
},
},
Timeline
| Task | Time |
|---|---|
| Custom Navigation | 0.5 days |
| Custom Dashboard with stats | 1–2 days |
| Custom Page (analytics, import, etc.) | 1–3 days |
| Full Admin UI redesign | 1–2 weeks |







