@devfamily/admiral
v6.0.0
Published
Admiral is a frontend framework for creating back office using React. It provides out-of-the-box components and tools that make developing an admin interface easy and fast.
Readme
Features
- 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
Tech Stack
React 19 • React Router 7 • Vite 6 • TypeScript 5.7 • @dnd-kit • @floating-ui • rc-* components • date-fns 4
Quick Start
Option 1 — scaffold a new project (recommended)
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.
Option 2 — add to an existing app
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.
Option 3 — run this repo
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/.
Core Concepts
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.
<Admin>
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>Routing
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 |
CRUD Pages
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/.
Data Provider
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:
// GET /api/users?page=1&page_size=10
{
"items": [{ "id": 1, "name": "John", "email": "[email protected]" }],
"meta": { "current_page": 1, "last_page": 5, "per_page": 10, "total": 50 }
}
// Validation errors (HTTP 422)
{
"errors": { "email": ["Email is required"] },
"message": "Validation failed"
}Auth Provider
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.
Forms & Inputs
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.
Filters
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.
Theming
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.
Localization
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>Module Imports
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'Hooks
| 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 |
Icons
Admiral uses React Icons (v5). Pass any icon name as a string or use JSX components directly.
Examples
- examples/express-server — Admiral + Express + Prisma, end to end
- Live demo — this repo's
pages/+src/running on mock data
Migrating from v5
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.
Using AI assistants
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."
Contributing
Fork the repo, make your changes, and submit a pull request. We welcome contributions!
License
MIT © dev.family
Contact
Questions or feedback: [email protected]
