A React framework for building admin panels and back-office applications.
Provides CRUD generation, authentication, theming, routing, and 30+ UI components out of the box.
Live Demo • Blog & Use Cases • Migrating from v5
- CRUD generation — define a resource config, get index/create/update pages with tables, forms, filters, and pagination
- 30+ UI components — Table, Form inputs, Select, DatePicker, Drawer, Tabs, Upload, Notification, and more
- Conditional & dependent fields —
visibleWhen/disabledWhen/requiredWhenrules on any input,Form.Whengroups, cascading selects - Validation-aware form tabs —
Form.Tabsshows per-tab error badges and switches to the first invalid tab on submit - File-based routing — pages directory maps to routes automatically (like Next.js)
- Authentication — login, logout, OAuth (Google, GitHub, Jira), session checks
- Theming — light/dark modes, fully customizable CSS presets
- Localization — built-in i18n with swappable locale packs
- TypeScript — full type coverage across all components and hooks
React 19 • React Router 7 • Vite 6 • TypeScript 5.7 • @dnd-kit • @floating-ui • rc-* components • date-fns 4
npx create-admiral-app@latestYou'll be prompted to choose:
- With Express.js backend — fully configured template with an API server
- Frontend only — just the admin panel, bring your own API
Then cd into the project, npm install, npm run dev — and you have a working admin panel.
Requires Node.js >= 20 and a bundler (the examples below use Vite).
1. Install the package and its peer dependencies:
npm install @devfamily/admiral react@^19 react-dom@^19 react-router-dom@^7 axios@^12. Create the app entry. Import the styles once, wrap everything in <Admin>:
// src/App.tsx
import { Admin, createRoutesFrom } from '@devfamily/admiral'
import '@devfamily/admiral/style.css'
import Menu from './menu'
import dataProvider from './dataProvider'
// createRoutesFrom takes a map of page modules; with Vite use import.meta.glob
const Routes = createRoutesFrom(import.meta.glob('../pages/**/*', { eager: true }))
export default function App() {
return (
<Admin dataProvider={dataProvider('/api')} menu={Menu}>
<Routes />
</Admin>
)
}3. Add a menu:
// src/menu.tsx
import { Menu, MenuItemLink } from '@devfamily/admiral'
export default function AppMenu() {
return (
<Menu>
<MenuItemLink icon="FiUsers" name="Users" to="/users" />
</Menu>
)
}4. Add a data provider — the object that tells Admiral how to talk to your API. Copy src/dataProvider.ts as a starting point; the contract it implements is described in Data Provider below.
5. Create your first page — see the CRUD example. Done: /users now renders
a table with forms, filters, and pagination.
git clone https://github.com/dev-family/admiral.git
cd admiral
yarn
yarn devOpen http://localhost:3000 — you'll see Admiral running on mock data, with the source for every page in pages/ and src/.
Admiral apps are built from a handful of pieces: an <Admin> root, file-based routing,
CRUD pages generated from a config, a data provider (and optionally an auth provider)
that connect Admiral to your API, plus forms, filters, themes, and locales.
Each piece is described below.
The root component. It wires up routing, theming, data fetching, auth, localization, and notifications — every other Admiral feature expects to live inside it.
<Admin
dataProvider={dataProvider(apiUrl)} // required: how to fetch data
menu={Menu} // required: sidebar menu component
authProvider={authProvider(apiUrl)} // optional: enables the login flow
themePresets={{ light, dark }} // optional: custom theme
locale={admiralLocales.ruRU} // optional: translations
logo={Logo} // optional: sidebar logo
oauthProviders={[OAuthProvidersEnum.Google]} // optional: OAuth buttons
>
<Routes />
</Admin>Admiral uses file-system based routing. Files in the pages/ directory map to routes:
| File | Route |
|---|---|
pages/index.tsx |
/ |
pages/users/index.tsx |
/users |
pages/users/create.tsx |
/users/create |
pages/users/[id].tsx |
/users/:id |
Define a resource in one file — Admiral generates the index, create, and update pages:
// pages/users/index.tsx
import { createCRUD, TextInput, SelectInput } from '@devfamily/admiral'
export const { IndexPage, CreatePage, UpdatePage } = createCRUD({
resource: 'users',
path: '/users',
index: {
title: 'Users',
newButtonText: 'Create User',
tableColumns: [
{ title: 'ID', dataIndex: 'id', key: 'id' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Email', dataIndex: 'email', key: 'email' },
],
},
form: {
create: {
fields: (
<>
<TextInput label="Name" name="name" required />
<TextInput label="Email" name="email" required />
<SelectInput label="Role" name="role_id" />
</>
),
},
edit: {
fields: (
<>
<TextInput label="Name" name="name" required />
<TextInput label="Email" name="email" required />
<SelectInput label="Role" name="role_id" />
</>
),
},
},
})
export default IndexPageThe full demo configs live in src/crud/.
The DataProvider interface defines how Admiral communicates with your API:
interface DataProvider {
getList: (resource, params) => Promise<{ items: any[]; meta: PaginationMeta }>
getOne: (resource, params) => Promise<{ data: any; values?: any }>
create: (resource, params) => Promise<{ data: any }>
update: (resource, params) => Promise<{ data: any }>
deleteOne: (resource, params) => Promise<{ data: any }>
getCreateFormData: (resource) => Promise<{ data?: any; values?: any }>
getUpdateFormData: (resource, params) => Promise<{ data: any; values?: any }>
getFiltersFormData: (resource, urlState?) => Promise<{ values?: any }>
reorderList: (resource, params) => Promise<void>
}See src/dataProvider.ts for a full implementation example.
API response format:
Pass an authProvider to <Admin> and Admiral renders a login page, guards all routes, and
keeps the session checked:
interface AuthProvider {
login: (params: { username: string; password: string }) => Promise<any>
logout: (params?: any) => Promise<void | false | string>
checkAuth: (params?: any) => Promise<void>
getIdentity: () => Promise<{ email: string; name: string }>
oauthLogin?: (provider: OAuthProvidersEnum) => Promise<{ redirect: string }>
oauthCallback?: (provider: OAuthProvidersEnum, data: string) => Promise<any>
}See src/authProvider.ts for a full implementation example.
CRUD forms are composed from ready-made inputs — each binds to a form value by name:
TextInput, MultilineTextInput, PasswordInput, SlugInput, SelectInput,
AjaxSelectInput, RadioInput, BooleanInput, DatePickerInput, DateRangePickerInput,
TimePickerInput, ColorPickerInput, EditorInput, FilePictureInput, DraggerInput,
ArrayInput, TranslatableInput
Inside any form (or a custom input) the useForm() hook gives access to values, errors, and
setters. Standalone forms outside CRUD are available via the Form component from
@devfamily/admiral/form.
Conditional & dependent fields. Every input takes visibleWhen / disabledWhen /
requiredWhen (a (values) => boolean or a JSON rule), <Form.When> reveals a group by one
rule, and AjaxSelectInput supports resetOnChangeOf cascades (e.g. country → city). See
Conditional & dependent fields.
Form.Tabs — validation-aware tabs. Long forms split into tabs that stay honest about
errors: each tab shows a badge with its invalid-field count, and a failed submit switches to
the first tab that needs attention (fields hidden by rules are skipped). See
Form.Tabs.
Add a filter drawer to a CRUD index page with the filter key, and promote any of the fields
to always-visible quick filters with quickFilters:
createCRUD({
// ...
filter: {
topToolbarButtonText: 'Filter',
fields: (
<>
<TextInput label="Name" name="name" />
<BooleanInput label="Active" name="is_active" />
</>
),
quickFilters: ['name', 'is_active'],
},
})Applied filters are reflected in the URL, so filtered views are shareable and bookmarkable. A complete example: src/crud/quickFilters.tsx.
Light and dark modes work out of the box (with a switcher in the header). To customize colors,
pass your own presets to <Admin>:
import themeLight from './theme/presets/themeLight'
import themeDark from './theme/presets/themeDark'
;<Admin themePresets={{ light: themeLight, dark: themeDark }}>
<Routes />
</Admin>Presets use @consta/uikit Theme under the hood. See admiral/theme/presets for the preset structure and CSS variables.
English is the default; a Russian pack ships in the box. Pass a ready-made pack to <Admin>,
or a partial object — any top-level section you provide replaces the default one:
import { Admin, admiralLocales } from '@devfamily/admiral'
;<Admin locale={admiralLocales.ruRU}>
<Routes />
</Admin>Admiral supports granular imports to reduce bundle size:
import { Admin, createCRUD, createRoutesFrom } from '@devfamily/admiral'
import { Table, Button, Select, Drawer } from '@devfamily/admiral/ui'
import { Form, TextInput, SelectInput } from '@devfamily/admiral/form'
import { useTheme } from '@devfamily/admiral/theme'
import '@devfamily/admiral/style.css'| Hook | Description |
|---|---|
useForm() |
Access form values, errors, options, and setters inside CRUD forms |
useTheme() |
Get and set the active theme (light / dark) |
useNav() |
Control sidebar collapse state and mobile burger menu |
useGetIdentity() |
Get the authenticated user's identity |
useLocaleProvider() |
Access the current locale configuration |
useUrlState() |
Read/write state synced to the URL query string |
Admiral uses React Icons (v5). Pass any icon name as a string or use JSX components directly.
- examples/express-server — Admiral + Express + Prisma, end to end
- Live demo — this repo's
pages/+src/running on mock data
v6 is a breaking release: React 19, React Router 7, ESM-only package, stricter types. The full checklist lives in MIGRATION.md. Migrating with an AI coding agent? Point it at MIGRATION_AI.md — the same guide as deterministic find → change → verify steps.
Two machine-oriented docs ship inside the npm package, so your agent can read them straight
from node_modules/@devfamily/admiral/:
- AI_GUIDE.md — an exact reference for generating Admiral code: the DataProvider contract and response shapes, where select/filter options come from, all 17 inputs, conditional-field rules, and the mistakes AI tools commonly make.
- MIGRATION_AI.md — an executable v5 → v6 migration playbook.
The demo site also serves an index of these docs at admiral.dev.family/llms.txt.
Example prompt: "Read node_modules/@devfamily/admiral/AI_GUIDE.md, then add a CRUD page for
the orders resource with a status filter and a conditional refund_reason field."
Fork the repo, make your changes, and submit a pull request. We welcome contributions!
MIT © dev.family
Questions or feedback: admiral@dev.family