Composable admin shell for React apps: Ant Design 6 layout, CRUD field system (lists, forms, filters, inlines, bulk actions), theme/density controls, AuthProvider + useAuth, data / permissions providers (react-admin–style naming, intentionally small), and React Router helpers.
Build an admin app and add a Users page — step-by-step from yarn create vite through CRUD and routes.
Interactive demo on GitHub Pages — sign in with admin / admin or user / user.
Install ding-react-admin plus its peer dependencies in one command — versions depend on whether you already have React. See docs/install.md (yarn and npm, new vs existing app).
Wrap your app with AuthProvider, then render AdminApp with navigation and routes:
import { AdminApp, AuthProvider, createSessionStorageAuthAdapter } from "ding-react-admin";
const navItems = [{ key: "home", label: "Home", path: "/" }];
const routes = [{ path: "/", element: <div>Welcome</div> }];
export function App() {
return (
<AuthProvider adapter={createSessionStorageAuthAdapter()}>
<AdminApp navItems={navItems} routes={routes} />
</AuthProvider>
);
}For CRUD pages, add DataProvider and PermissionsProvider — see Getting started and docs/quick-start.md.
- Declarative CRUD — describe list and form pages in JSX; field
sourcenames map to your API - List tables with column sorting, pagination, and URL-synced query state
- Filters — text, number, boolean, date, select, and reference lookups on list pages
- Page forms and modal forms (
ResourceForm,ResourceFormModal) with shared create/edit logic - Tabbed forms (
FormTabs) and stepped forms (FormSteps) for long or wizard-style records - Reference / lookup fields with inline create and edit actions in a modal
- Inline nested rows — tabular (
InlineFormSet) and stacked layouts for related records - Bulk actions — Django-style row selection and batch operations
- Built-in fields — text, number, boolean, date, select, password, image, file, reference, reference-many
- Permissions — gate list/form actions and nav items per resource (
useCan,usePermissions) - Auth & routing —
AuthProvider, login page,Protected/GuestOnlyroute guards - Theme & density — light/dark mode and compact/comfortable layout switches
- Validation errors — map API responses to field errors (Django REST, .NET, Node helpers included)
- Data layer —
DataProvidercontract plus REST and in-memory handler factories - Uploads — image/file fields with
FormDatasubmit support
From the playground demo:
List with filters
Tabbed form page
Stepped modal form
Login
Wire a DataProvider (see Providers below), then describe list and form pages in JSX — field source names map to your API. Filters, modals, permissions, and validation plug in when you need them; start with the basics:
import { ResourceList, TextColumn, DateColumn } from "ding-react-admin";
export function InvoiceListPage() {
return (
<ResourceList resource="invoices" title="Invoices" pathPrefix="/invoices">
<TextColumn source="number" label="Number" />
<TextColumn source="customer" label="Customer" />
<DateColumn source="issuedAt" label="Issued" />
</ResourceList>
);
}Add <TextFilter />, <ReferenceFilter />, bulk actions, and row permissions inside the same component — list pages guide.
import { ResourceForm, TextField, DateField } from "ding-react-admin";
export function InvoiceFormPage() {
return (
<ResourceForm resource="invoices" title="Invoice" listPath="/invoices">
<TextField source="number" label="Number" required />
<TextField source="customer" label="Customer" required />
<DateField source="issuedAt" label="Issued" required />
</ResourceForm>
);
}Tabbed forms, stepped modals, and API validation errors under fields (fetch, axios, OpenAPI) — forms guide · validation errors.
Django-style tabular inlines: related rows edit in a table inside the parent form.
import {
InlineFormSet,
NumberField,
ResourceForm,
TextField,
} from "ding-react-admin";
<ResourceForm resource="invoices" title="Invoice" listPath="/invoices">
<TextField source="number" label="Number" required />
<TextField source="customer" label="Customer" required />
<InlineFormSet
field="lines"
label="Lines"
columns={[
{
source: "label",
label: "Label",
cell: ({ name }) => (
<TextField source="label" name={name} hideLabel required />
),
},
{
source: "quantity",
label: "Qty",
width: 120,
cell: ({ name }) => (
<NumberField source="quantity" name={name} hideLabel required min={0} />
),
},
{
source: "unitPrice",
label: "Unit price",
cell: ({ name }) => (
<NumberField
source="unitPrice"
name={name}
hideLabel
required
min={0}
step={0.01}
/>
),
},
]}
/>
</ResourceForm>If you already use react-hook-form, you can pick this up in minutes — nested inlines are just useFieldArray on the same record. See inlines guide.
Nothing is wired automatically except theme inside <AdminApp />. Wrap providers yourself:
| Provider | Required for |
|---|---|
AuthProvider |
Login, logout, route guards (Protected, GuestOnly), useAuth |
DataProvider |
CRUD components (ResourceList, ResourceForm, …) |
PermissionsProvider |
Permission gating in CRUD (usePermissions, useCan) |
Auth only (custom pages, no CRUD yet):
<AuthProvider adapter={createSessionStorageAuthAdapter()}>
<AdminApp navItems={nav} routes={routes} />
</AuthProvider>Full stack (CRUD + permissions) — see docs/data-permissions.md and examples/playground/src/main.tsx:
<AuthProvider adapter={authAdapter}>
<DataProvider value={dataProvider}>
<PermissionsProvider can={permissions}>
<AdminApp navItems={nav} routes={routes} />
</PermissionsProvider>
</DataProvider>
</AuthProvider>Use createSessionStorageAuthAdapter for demos; replace with an adapter that calls your API in production. Implement AuthAdapter.login with a LoginCredentials object (username, password, plus any extra fields your login form needs, e.g. businessId). Optionally implement getUserLabel so the account menu shows the logged-in user — see data-permissions.md.
New to CRUD: docs/tutorial-one-entity.md — full walkthrough from yarn create vite through Users list/form, data-provider.ts, and routes.
Quick path: docs/quick-start.md — <AuthProvider> + <AdminApp /> with declarative routes.
Full control: docs/composition.md and the playground — your own createBrowserRouter with AdminLayout, Protected, GuestOnly, DataProvider, and PermissionsProvider.
| Topic | Guide |
|---|---|
| Install & peer deps | docs/install.md |
| Tutorial: add one CRUD entity | docs/tutorial-one-entity.md |
| Example playground app | docs/example-app.md |
| Sidebar navigation | docs/navigation.md |
| CRUD overview | docs/crud/overview.md |
| List pages & filters | docs/crud/list-pages.md |
| Bulk actions (Django-style) | docs/crud/bulk-actions.md |
| Form pages | docs/crud/forms.md |
| Reference / lookup fields | docs/crud/references.md |
| Inline nested forms | docs/crud/inlines.md |
| Custom field types | docs/crud/custom-fields.md |
| Data layer & permissions | docs/data-permissions.md |
| Form validation errors | docs/form-validation-errors.md |
| Routing & auth guards | docs/routing.md |
| Login / register layout | docs/auth-pages.md |
Quick start (<AdminApp />) |
docs/quick-start.md |
| Composition (your own router) | docs/composition.md |
| Odoo-style app hub | docs/app-hub.md |
createAdminRouter shortcut |
docs/admin-router.md |
| Developing next to your app (Vite) | docs/developing.md |
MIT




