Разработка кастомных Views KeystoneJS
KeystoneJS Admin UI построен на React и позволяет полностью заменять или расширять интерфейс через кастомные Views. Это не просто тема — можно переопределить компоненты для конкретных List, добавить собственные страницы, изменить навигацию и дашборд.
Типы View-расширений
1. Custom Pages — полностью кастомные страницы в Admin UI на произвольных путях.
2. Custom Navigation — замена стандартного меню.
3. List Views — переопределение компонентов для конкретного List.
4. Field Views — компоненты для полей (рассматриваются в разделе Custom Fields).
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>
{/* Группировка по разделам */}
<div className="mt-4 mb-1 px-3 text-xs font-semibold text-gray-400 uppercase">
Контент
</div>
<ListNavItems lists={lists} include={['Post', 'Category', 'Tag']} />
<div className="mt-4 mb-1 px-3 text-xs font-semibold text-gray-400 uppercase">
Пользователи
</div>
<ListNavItems lists={lists} include={['User', 'Role']} />
<NavItem href="/admin/analytics">Аналитика</NavItem>
</NavigationContainer>
);
}
Custom Dashboard
// admin/pages/index.tsx — переопределяет стандартный дашборд
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="Панель управления">
<div className="grid grid-cols-3 gap-4 mb-8">
<StatCard label="Опубликованных постов" value={data?.postsCount} loading={loading} />
<StatCard label="Черновиков" value={data?.draftsCount} loading={loading} />
<StatCard label="Пользователей" value={data?.usersCount} loading={loading} />
</div>
<RecentPostsTable />
</PageContainer>
);
}
Custom Pages с произвольными маршрутами
// admin/pages/analytics.tsx
import { PageContainer } from '@keystone-6/core/admin-ui/components';
export default function AnalyticsPage() {
return (
<PageContainer header="Аналитика">
{/* Встраиваем Recharts, Chart.js или iframe Metabase */}
<SalesChart />
<TopContentTable />
</PageContainer>
);
}
Маршрут будет доступен по /admin/analytics автоматически — Next.js файловая маршрутизация работает в Admin UI.
Переопределение Item View для конкретного List
Можно полностью заменить страницу редактирования конкретного элемента:
// В конфиге List:
ui: {
itemView: {
// Кастомный компонент через путь к файлу
// Используется экспериментальная фича KeystoneJS
defaultFieldMode: 'read', // 'hidden' | 'read' | 'edit'
},
},
Добавление кастомных экшенов в список
Через extendGraphqlSchema + кастомный UI-компонент:
// Кнопка "Опубликовать всё" в ListPage
import { useMutation, gql } from '@keystone-6/core/admin-ui/apollo';
const PUBLISH_ALL = gql`
mutation PublishAllDrafts {
publishAllDrafts { count }
}
`;
export function PublishAllButton() {
const [publishAll, { loading }] = useMutation(PUBLISH_ALL);
return (
<button
onClick={() => publishAll().then(() => window.location.reload())}
disabled={loading}
>
{loading ? 'Публикуем...' : 'Опубликовать все черновики'}
</button>
);
}
Сроки разработки
| Задача | Время |
|---|---|
| Custom Navigation | 0.5 дня |
| Custom Dashboard со статистикой | 1–2 дня |
| Custom Page (аналитика, импорт, etc.) | 1–3 дня |
| Полный редизайн Admin UI | 1–2 недели |







